Creating a library of building blocks

A library can be created using pre-designed building blocks and use them in hierarchical designs. This can be achieved using keyword import. In this case, ACT file begins with a sequence of “import” statements. The “import” looks for file in the current directory first, then in the colon-separated path specified by $ACT_PATH, and finally in $ACT_HOME/act.

ACT files can be imported in two ways:

(a) For simple projects, design files containing building blocks can be imported using keyword import. For example, the gates.act file contains basic logic gates.

defproc nand2 (bool? a,b; bool! y)
{
  prs {
    a & b => y-
  }
}
 
defproc and2 (bool? a,b; bool! y)
{
  bool _y;
  prs {
    a & b => _y-
    _y => y-
  }
}
 
defproc or2 (bool? a,b; bool! y)
{
  bool _y;
  prs {
    a | b => _y-
    _y => y-
  }
}

This file can be imported in a new ACT file xor2.act for creating XOR gate using basic logic gates.

import "gates.act";
 
defproc xor2 (bool? a,b; bool! y)
{
  bool y1, y2;
  nand2 N1 (a,b,y1);
  or2 O1 (a,b,y2);
  and2 A1 (y1,y2,y);
}
 
xor2 x;

(b) For complex projects involving a large number of ACT files, importing libraries can become complicated. For example, a project can have multiple ACT files containing different implementation of a design with same process name or different channels with same name. To keep names of process, channel, and types separate in such cases, ACT provides the option of namespace (ACT namespaces).

The following example creates a namespace gates in file gates.act and defines process for basic logic gates within the namespace.

namespace gates
{
export defproc xor2 (bool? a,b; bool! y)
{
  bool _a, _b;
  prs{
    a => _a-
    b => _b-
    (a & b) | (_a & _b) => y-
  }
}
 
export defproc and2 (bool? a,b; bool! y)
{
  bool _y;
  prs {
    a & b => _y-
    _y => y-
  }
}
 
export defproc or2 (bool? a,b; bool! y)
{
  bool _y;
  prs {
    a | b => _y-
    _y => y-
  }
}
}

As shown below, this library is imported in a new ACT file adder.act to design full adder using logic gates defined in the library.

import "gates.act";
 
defproc adder (bool? a,b,ci; bool! s,co)
{
  bool y1,y2,y3;
  gates::xor2 X1(a, b, y1);
  gates::and2 A1(a,b,y2);
  gates::xor2 X2 (y1,ci,s);
  gates::and2 A2(y1,ci,y3);
  gates::or2 O1(y2,y3,co);
}
 
adder FA;

Finally, ACT supports another import format that is similar to those provided by object-oriented languages like Python and Java. Since gates.act defines the namespace gates, it can be imported as follows:

import gates;
 
defproc adder (bool? a,b,ci; bool! s,co)
{
  bool y1,y2,y3;
  gates::xor2 X1(a, b, y1);
  gates::and2 A1(a,b,y2);
  gates::xor2 X2 (y1,ci,s);
  gates::and2 A2(y1,ci,y3);
  gates::or2 O1(y2,y3,co);
}
 
adder FA;

The import statement looks for gates.act (details are in Namespaces), and imports it if it is found. It also checks that the namespace gates exists after the import.

Finally, if you are going to be using the gates namespace a lot, it can be added to the search path used to find type definitions as follows.

import gates;
open gates;
 
defproc adder (bool? a,b,ci; bool! s,co)
{
  bool y1,y2,y3;
  xor2 X1(a, b, y1);
  and2 A1(a,b,y2);
  xor2 X2 (y1,ci,s);
  and2 A2(y1,ci,y3);
  or2 O1(y2,y3,co);
}
 
adder FA;

Simulating with prsim script

Run simulation in prsim with script as:

prsim adder.act < adder.scr

You could also run prsim and then source script as:

prsim adder.act
(Prsim) source adder.scr

where adder.scr file contains the following commands:

initialize

set fa.a 0
set fa.b 0
set fa.ci 0
cycle
get fa.a
get fa.b
get fa.ci
get fa.s
get fa.co

set fa.a 0
set fa.b 0
set fa.ci 1
cycle
get fa.a
get fa.b
get fa.ci
get fa.s
get fa.co