| #!/usr/bin/perl |
| # simple Clock Tree Syntheizer |
| # By: M. Shalan, Dec 2019 |
| # |
| # usage: ./cts.pl <netlist.v> <fanout> <clock_net_name> |
| # |
| # You need to set the following variables based on the PDK/CSL |
| # root_clkbuf : CT root buffer type |
| # clkbuf : CT branching buffer type |
| # clkbuf_in : Buffer input pin name |
| # clkbuf_out : Buffer output pin name |
| # |
| # Copyright (c) Efabless Corporation. All rights reserved. |
| # See LICENSE file in the project root for full license information. |
| # |
| use POSIX; |
| use Switch; |
| my $leaves;# = $ARGV[0]; |
| my $vlg_fn = $ARGV[0]; |
| my $fanout = $ARGV[1]; |
| my $clk_net = $ARGV[2]; |
| my $root_clkbuf = $ARGV[3]; |
| my $clkbuf = $ARGV[4]; |
| my $clkbuf_in = $ARGV[5]; |
| my $clkbuf_out = $ARGV[6]; |
| unless (-e $vlg_fn) { |
| print "$vlg_fn File Doesn't Exist!"; |
| exit 0; |
| } |
| $leaves = `grep "($clk_net)" $vlg_fn | wc -l`; |
| my $levels = logb($leaves, $fanout); |
| # This array holds the number of buffers @ each level |
| #@buffs = (0) x ($levels + 1); |
| @buffs = (0) x 20; |
| $buffs[0] = $leaves; |
| @vlg_wires = (); |
| @vlg_cells = (); |
| %def_nets; |
| @def_comp = (); |
| # Verilog o/p |
| my $cell_cnt = 0; |
| for my $l (1..$levels-1) { |
| $l1 = $l - 1; |
| $inst = "_CTS_buf_".$l."_"; |
| $owire = "clk_$l1"."_"; |
| $iwire = "clk_$l"."_"; |
| for(my $i = 0; $i < $leaves; $i+=($fanout**$l)){ |
| $ii = floor($i/($fanout**($l+1))) * ($fanout**($l+1)); |
| my $cell_nm = "$inst".$i; |
| push @vlg_cells, "$clkbuf $cell_nm ( .$clkbuf_in($iwire$ii), .$clkbuf_out($owire$i) );\n"; |
| push @vlg_wires, "wire $owire$i;\n"; |
| $buffs[$l]++; |
| $cell_cnt++; |
| } |
| print "\n"; |
| } |
| my $root_net = "clk_".($levels-1)."_0"; |
| push @vlg_cells, "$root_clkbuf _CTS_root ( .$clkbuf_in($clk_net), .$clkbuf_out($root_net) );\n"; |
| push @vlg_wires, "wire clk_".($levels-1)."_0;\n"; |
| # Insert the newly created wires and buufers into the netlist |
| open(FH, '<', $vlg_fn) or die $!; |
| my $flag = 0; |
| my $ff_cnt = 0; |
| while(<FH>){ |
| switch ($flag){ |
| case 0 { $flag = 1 if($_ =~ /wire/); print $_} |
| case 1 { |
| if(!($_ =~ /wire/) && !($_ =~ /input/) && !($_ =~ /output/)) { |
| $flag = 2; |
| print "\n// CTS added wires\n"; |
| print @vlg_wires; |
| print "\n// CTS added buffers\n"; |
| print @vlg_cells; |
| print "\n"; |
| print $_; |
| } else { |
| print $_; |
| } |
| } |
| case 2 { |
| if($_ =~ /\($clk_net\)/) { |
| my $clk_wire_name = "clk_0_".int($ff_cnt / $fanout) * $fanout; |
| #print "$clk_wire_name \n"; |
| $_ =~ s/$clk_net/$clk_wire_name/g; |
| #print $_; |
| $ff_cnt++; |
| } |
| print $_; |
| } |
| } |
| } |
| close(FH); |
| sub logb { |
| my $n = shift; |
| my $b = shift; |
| return ceil(log($n)/log($b)); |
| } |
| |