Introduction

This guide is intended to explain how to export asynchronous circuits designed in ACT and import them in Cadence Virtuoso to simulate their functioning. Some steps are needed to get the Verilog netlist and some editing to let the netlist be valid and more readable. These steps are shown referring to one of the most basic circuits starting from its behavioral description.

Example: FIFO boolean buffer

Consider as a starting point an already designed block, which has to be simulated in Cadence Virtuoso. This tutorial will refer to the one of the most basic circuits: the FIFO boolean buffer. It can be described in ACT using the CHP sub-language as follows:

fifobool.act
defproc bool_buff (chan?(bool) L; chan!(bool) R)
{
   bool x;
 
   chp {
       *[ L?x; R!x ]
  }
}

The file is saved under the name fifobool.act.

The first step consists in the synthesis of the process. This is done with the command chp2prs. Chp2prs maps the synthesized processes using the ACT implementation relation and refine { … } body. It takes as inputs the act file, which contains the CHP description of the circuit, and the name of the top-level process that has to be synthesized, and returns an output file which contains the synthesized design with .act extension.

Thus:

>chp2prs fifobool.act bool_buff fifobool_chp2prs.act

Other options could be added to select bundled-data (-b) or to specify a logic optimizator (-o abc). The output is:

>cat fifobool_chp2prs.act
import "fifobool.act";
import "syn/qdi/_all_.act";
open syn;
import "expr.act";
 
defproc sdt_bool_buff <: bool_buff ()
+{
 syn::sdtboolvar x;
  syn::sdtboolchan R;
  syn::sdtboolchan L;
}
{
 refine {
   syn::var_bool_ports<1,1> var_x(x);
   syn::mux_bool_outport<1> R_muxo(R);
   syn::mux_bool_inport<1> L_muxi(L);
   a1of1 c0;
   syn::expr::const<1,1> e0;
   a1of1 c1;
   a1of1 c2;
   syn::recvport<1,1> s_0(c2,L_muxi.m[0],var_x.in[0]);
   a1of1 c3;
  syn::expr::nullint<1> e1(var_x.out[0]);
   syn::expr::blk0 e2(e1.out);
   syn::transfer<1> s_1(c3, e2.out,R_muxo.m[0]);
   syn::semi<2> s_2(c1,{c2,c3});
   syn::loop<1> s_3(c0,{e0.out},{c1});
/*--- connect reset to go signal ---*/
   a1of1 c4;
   syn::sinit s4 (c0);
 }
}

The names of the synthesized version are prefixed with sdt_ which stands for syntax-directed translation. Then, in order to achieve the cell-mapping the interact tool is launched in this way:

>interact -ref=1 

Note that a technology configuration could have been specified by adding the option -Ttechname. It changes the technology configuration files to techname. This specifies that the config files are in directory $ACT_HOME/conf/techname. More info here.

If none is specified as in this case, the default configuration located at the following path $ACT_HOME/conf/generic/ is automatically selected. More info here.

The option -ref=1 is used to select the refinement level. -ref=number: select refinement body, if it exists. Refinements can be nested, so the number specifies the number of refinement levels that should be expanded. The standard use case is -ref=1. More info here.

interact> act:read fifobool_chp2prs.act
interact> act:localize Reset
interact> act:expand
interact> act:top sdt_bool_buff

This snippet of code reads in the ACT design, localize the signal Reset ( it provides a single pin for the Reset by shorting the reset pins of all the sub-blocks), expand/elaborates the design and sets the top level of the design as sdt_bool_buff. Next, production rules are mapped to cells:

interact> ckt:cell-map
WARNING: new cells generated; please update your cell library.
(Use ckt:cell-save to see the new cells.) New cell names are:
   ginvx0
   gnor2x0
   g1n_0x0
   gcelem2x0
   gpd1x0
   gac3x0
   g0n2na_01a23aox0
   g0n1n2naa_01ox0
   gac1x0
   

Since this design was started without a cell library, this report indicates the list of fresh cells that are needed to implement the design. To see the production rules for each of these cells, a cell namespace file can be saved as follows:

interact> ckt:cell-save fifobool_cell_export.act

Finally, a Verilog netlist can be exported . The option -nocell is used to avoid cells definition in the netlist.

interact> ckt:save-vnet -nocell fifobool_verilog_netlist.v

More info here.

In order to make the netlist valid, reg and wire definitions must be erased. It can be done using regex:

  • Replace '^ +reg .+' with ' '
  • Replace '^ +wire .+' with ' '

To also make it more readable:

  • Replace '^_0_0' with ' '
  • Replace '^ cell_0_0' with ' '
  • Replace '_0_0' with '_'
  • Replace '__' with '_'
  • Replace '_syn' with 'syn'
  • Replace '_std' with 'std'
  • Replace '([ \t]*\n){3,}' with ' ' - eliminating empty gaps
  • Erase all empty modules (manually).

Once this is done, the netlist is ready to be imported in Cadence Virtuoso (File → Import → Verilog) and should look like this:

fifobool_verilog_netlist.v
//
// Verilog module for: loop<1>
//
module syn_loop_31_4(\c.d.d[0] , \g[0].r , \s[0].a );
   input \c.d.d[0] ;
   output \g[0].r ;
   input \s[0].a ;
 
// -- signals ---
 
// --- instances
 ginvx0 \cx0  (.\in[0] (\c.d.d[0] ), .out(_r));
 gnor2x0 \cx1  (.\in[0] (_r), .\in[1] (\s[0].a ), .out(\g[0].r ));
endmodule
 
//
// Verilog module for: sinit<>
//
module syn_sinit(\c.d.d[0] , \c.a , Reset);
   output \c.d.d[0] ;
   input \c.a ;
   input Reset;
 
// --- instances
 g1n_0x0 \cx2  (.\in[0] (Reset), .\in[1] (_final_sig), .out(final_sig));
 gnor2x0 \cx0  (.\in[0] (Reset), .\in[1] (final_sig), .out(\c.d.d[0] ));
 ginvx0 \cx1  (.\in[0] (\c.a ), .out(_final_sig));
endmodule
 
//
// Verilog module for: recvport<1,1>
//
module syn_recvport_31_71_4(\c.d.d[0] , \ch.d.d[0].d[0] , \ch.d.d[0].d[1] , \port.d.d[0].d[0] , \port.d.d[0].d[1] );
   input \c.d.d[0] ;
   input \ch.d.d[0].d[0] ;
   input \ch.d.d[0].d[1] ;
   output \port.d.d[0].d[0] ;
   output \port.d.d[0].d[1] ;
 
// -- signals --- // --- instances
 ginvx0 \cx2  (.\in[0] (\_t[0] ), .out(\port.d.d[0].d[1] ));
 gcelem2x0 \cx0  (.\in[0] (\c.d.d[0] ), .\in[1] (\ch.d.d[0].d[1] ), .out(\_t[0] ));
 ginvx0 \cx3  (.\in[0] (\_f[0] ), .out(\port.d.d[0].d[0] ));
 gcelem2x0 \cx1  (.\in[0] (\c.d.d[0] ), .\in[1] (\ch.d.d[0].d[0] ), .out(\_f[0] ));
endmodule
 
//
// Verilog module for: const<1,1>
//
module syn_expr_const_31_71_4(\out.r , \out.d.d[0].d[0] , \out.d.d[0].d[1] , Reset);
   input \out.r ;
   output \out.d.d[0].d[0] ;
   output \out.d.d[0].d[1] ;
   input Reset;
 
// -- signals --- // --- instances
 ginvx0 \cx0  (.\in[0] (\out.r ), .out(_req));
 gpd1x0 \cpx0  (.\in[0] (Reset), .out(\out.d.d[0].d[0] ));
 ginvx0 \cx1  (.\in[0] (_req), .out(\out.d.d[0].d[1] ));
endmodule
 
//
// Verilog module for: var_init<1,f>
//
module syn_var_init_31_7f_4(\v.d[0].d[0] , \v.d[0].d[1] , \wt[0] , \wf[0] , Reset);
   output \v.d[0].d[0] ;
   output \v.d[0].d[1] ;
   input \wt[0] ;
   input \wf[0] ;
   input Reset;
 
// -- signals --- // --- instances
 syn_var_one_bit_3f_4 \bits[0]  (.wt(\wt[0] ), .wf(\wf[0] ), .dt(\v.d[0].d[1] ), .df(\v.d[0].d[0] ), .Reset(Reset));
endmodule
 
//
// Verilog module for: readport<1>
//
module syn_expr_readport_31_4(\var.d[0].d[0] , \var.d[0].d[1] , \out.r , \out.d.d[0].d[0] , \out.d.d[0].d[1] );
   input \var.d[0].d[0] ;
   input \var.d[0].d[1] ;
   input \out.r ;
   output \out.d.d[0].d[0] ;
   output \out.d.d[0].d[1] ;
 
// -- signals --- // --- instances
 gac3x0 \cx2  (.\in[0] (xout), .\in[1] (\var.d[0].d[0] ), .out(\out.d.d[0].d[1] ));
 ginvx0 \cx0  (.\in[0] (\out.r ), .out(xout));
 gac3x0 \cx1  (.\in[0] (xout), .\in[1] (\var.d[0].d[1] ), .out(\out.d.d[0].d[0] ));
endmodule
 
//
// Verilog module for: writeport<1>
//
module syn_expr_writeport_31_4(\wt[0] , \wf[0] , \var.d[0].d[0] , \var.d[0].d[1] , \in.d.d[0].d[0] , \in.d.d[0].d[1] , \in.a , Reset);
   output \wt[0] ;
   output \wf[0] ;
   input \var.d[0].d[0] ;
   input \var.d[0].d[1] ;
   input \in.d.d[0].d[0] ;
   input \in.d.d[0].d[1] ;
   output \in.a ;
   input Reset;
 
// -- signals --- // --- instances
 g0n2na_01a23aox0 \cx4  (.\in[0] (\wt[0] ), .\in[1] (\var.d[0].d[1] ), .\in[2] (\wf[0] ), .\in[3] (\var.d[0].d[0] ), .out(\cx4.out ));
 ginvx0 \cx2  (.\in[0] (\cx0.out ), .out(\wt[0] ));
 ginvx0 \cx0  (.\in[0] (\in.d.d[0].d[1] ), .out(\cx0.out ));
 gnor2x0 \cx5  (.\in[0] (Reset), .\in[1] (\cx4.out ), .out(\in.a ));
 ginvx0 \cx3  (.\in[0] (\cx1.out ), .out(\wf[0] ));
 ginvx0 \cx1  (.\in[0] (\in.d.d[0].d[0] ), .out(\cx1.out ));
endmodule
 
//
// Verilog module for: var_int_ports<1,1,1>
//
module syn_var_int_ports_31_71_71_4(\v.d[0].d[0] , \v.d[0].d[1] , \in[0].d.d[0].d[0] , \in[0].d.d[0].d[1] , \in[0].a , \out[0].r , \out[0].d.d[0].d[0] , \out[0].d.d[0].d[1] , Reset);
   output \v.d[0].d[0] ;
   output \v.d[0].d[1] ;
   input \in[0].d.d[0].d[0] ;
   input \in[0].d.d[0].d[1] ;
   output \in[0].a ;
   input \out[0].r ;
   output \out[0].d.d[0].d[0] ;
   output \out[0].d.d[0].d[1] ;
   input Reset;
 
// -- signals --- // --- instances
 syn_var_init_31_7f_4 \x  (.\v.d[0].d[0] (\v.d[0].d[0] ), .\v.d[0].d[1] (\v.d[0].d[1] ), .\wt[0] (\w.wt[0] ), .\wf[0] (\w.wf[0] ), .Reset(Reset));
 syn_expr_readport_31_4 \r[0]  (.\var.d[0].d[0] (\v.d[0].d[0] ), .\var.d[0].d[1] (\v.d[0].d[1] ), .\out.r (\out[0].r ), .\out.d.d[0].d[0] (\out[0].d.d[0].d[0] ), .\out.d.d[0].d[1] (\out[0].d.d[0].d[1] ));
 syn_expr_writeport_31_4 \w  (.\wt[0] (\w.wt[0] ), .\wf[0] (\w.wf[0] ), .\var.d[0].d[0] (\v.d[0].d[0] ), .\var.d[0].d[1] (\v.d[0].d[1] ), .\in.d.d[0].d[0] (\in[0].d.d[0].d[0] ), .\in.d.d[0].d[1] (\in[0].d.d[0].d[1] ), .\in.a (\in[0].a ), .Reset(Reset));
endmodule
 
//
// Verilog module for: var_bool_ports<1,1>
//
module syn_var_bool_ports_31_71_4(\v.x.d[0].d[0] , \v.x.d[0].d[1] , \in[0].d.d[0].d[0] , \in[0].d.d[0].d[1] , \in[0].a , \out[0].r , \out[0].d.d[0].d[0] , \out[0].d.d[0].d[1] , Reset);
   output \v.x.d[0].d[0] ;
   output \v.x.d[0].d[1] ;
   input \in[0].d.d[0].d[0] ;
   input \in[0].d.d[0].d[1] ;
   output \in[0].a ;
   input \out[0].r ;
   output \out[0].d.d[0].d[0] ;
   output \out[0].d.d[0].d[1] ;
   input Reset;
 
// -- signals --- // --- instances
 syn_var_int_ports_31_71_71_4 \x  (.\v.d[0].d[0] (\v.x.d[0].d[0] ), .\v.d[0].d[1] (\v.x.d[0].d[1] ), .\in[0].d.d[0].d[0] (\in[0].d.d[0].d[0] ), .\in[0].d.d[0].d[1] (\in[0].d.d[0].d[1] ), .\in[0].a (\in[0].a ), .\out[0].r (\out[0].r ), .\out[0].d.d[0].d[0] (\out[0].d.d[0].d[0] ), .\out[0].d.d[0].d[1] (\out[0].d.d[0].d[1] ), .Reset(Reset));
endmodule
 
//
// Verilog module for: fullseq<>
//
module syn_fullseq(\go.d.d[0] , \go.a , \r.d.d[0] , \r.a , Reset);
   input \go.d.d[0] ;
   output \go.a ;
   output \r.d.d[0] ;
   input \r.a ;
   input Reset;
 
// -- signals --- // --- instances
 ginvx0 \cx4  (.\in[0] (_goa), .out(\go.a ));
 gac1x0 \cx2  (.\in[0] (x), .\in[1] (_ra), .out(_goa));
 g0n1n2naa_01ox0 \cx0  (.\in[0] (Reset), .\in[1] (x), .\in[2] (_gor), .out(\r.d.d[0] ));
 ginvx0 \cx5  (.\in[0] (\r.a ), .out(_ra));
 ginvx0 \cx3  (.\in[0] (\go.d.d[0] ), .out(_gor));
 g1n_0x0 \cx1  (.\in[0] (_gor), .\in[1] (_ra), .out(x));
endmodule
 
//
// Verilog module for: semi<2>
//
module syn_semi_32_4(\c.d.d[0] , \c.a , \stmts[0].d.d[0] , \stmts[0].a , \stmts[1].d.d[0] , \stmts[1].a , Reset);
   input \c.d.d[0] ;
   output \c.a ;
   output \stmts[0].d.d[0] ;
   input \stmts[0].a ;
   output \stmts[1].d.d[0] ;
   input \stmts[1].a ;
   input Reset;
 
// -- signals --- // --- instances
 syn_fullseq \f[0]  (.\go.d.d[0] (\c.d.d[0] ), .\go.a (\f[0].go.a ), .\r.d.d[0] (\stmts[0].d.d[0] ), .\r.a (\stmts[0].a ), .Reset(Reset));
 syn_fullseq \f[1]  (.\go.d.d[0] (\f[0].go.a ), .\go.a (\c.a ), .\r.d.d[0] (\stmts[1].d.d[0] ), .\r.a (\stmts[1].a ), .Reset(Reset));
endmodule
 
//
// Verilog module for: sdt_bool_buff<>
//
module sdt_bool_buff(\L.x.d.d[0].d[0] , \L.x.d.d[0].d[1] , \L.x.a , \R.x.d.d[0].d[0] , \R.x.d.d[0].d[1] , \R.x.a , Reset);
   input \L.x.d.d[0].d[0] ;
   input \L.x.d.d[0].d[1] ;
   output \L.x.a ;
   output \R.x.d.d[0].d[0] ;
   output \R.x.d.d[0].d[1] ;
   input \R.x.a ;
   input Reset;
 
// -- signals --- // --- instances
 syn_loop_31_4 \s_3  (.\c.d.d[0] (\c0.d.d[0] ), .\g[0].r (\s_3.g[0].r ), .\s[0].a (\c1.a ));
 syn_sinit \s4  (.\c.d.d[0] (\c0.d.d[0] ), .\c.a (\c0.a ), .Reset(Reset));
 syn_recvport_31_71_4 \s_0  (.\c.d.d[0] (\c2.d.d[0] ), .\ch.d.d[0].d[0] (\L.x.d.d[0].d[0] ), .\ch.d.d[0].d[1] (\L.x.d.d[0].d[1] ), .\port.d.d[0].d[0] (\s_0.port.d.d[0].d[0] ), .\port.d.d[0].d[1] (\s_0.port.d.d[0].d[1] ));
 syn_expr_const_31_71_4 \e0  (.\out.r (\s_3.g[0].r ), .\out.d.d[0].d[0] (\c0.a ), .\out.d.d[0].d[1] (\c1.d.d[0] ), .Reset(Reset));
 syn_var_bool_ports_31_71_4 \var_x  (.\v.x.d[0].d[0] (\x.x.d[0].d[0] ), .\v.x.d[0].d[1] (\x.x.d[0].d[1] ), .\in[0].d.d[0].d[0] (\s_0.port.d.d[0].d[0] ), .\in[0].d.d[0].d[1] (\s_0.port.d.d[0].d[1] ), .\in[0].a (\L.x.a ), .\out[0].r (\c3.r ), .\out[0].d.d[0].d[0] (\R.x.d.d[0].d[0] ), .\out[0].d.d[0].d[1] (\R.x.d.d[0].d[1] ), .Reset(Reset));
 syn_semi_32_4 \s_2  (.\c.d.d[0] (\c1.d.d[0] ), .\c.a (\c1.a ), .\stmts[0].d.d[0] (\c2.d.d[0] ), .\stmts[0].a (\L.x.a ), .\stmts[1].d.d[0] (\c3.r ), .\stmts[1].a (\R.x.a ), .Reset(Reset));
endmodule