MIDI
[mw/milkymist.git] / tools / xdlanalyze.pl
1 #!/usr/bin/perl -w
2 #
3 # xdlanalyze.pl - A script to view some statistics about XDL-files
4 #
5 # Copyright (c) 2006 Andreas Ehliar <ehliar@isy.liu.se>
6 # You may copy or modify this program under the terms of the GNU
7 # General Public License (version 2 or later)
8 #
9 # Usage: xdlanalyze.pl foo.xdl [hierarchy levels]
10 #
11 # Example usage:
12 # $ perl xdlanalyze.pl dafk.xdl
13 # XDLAnalyze V1.1 by Andreas Ehliar <ehliar@isy.liu.se>
14 # Analyzing the file dafk.xdl...................................................
15 # +-------------+--------+--------+--------+-----------+--------+--------+
16 # | Module      |   LUTS |     FF | RAMB16 | MULT18x18 |    IOB |    DCM |
17 # +-------------+--------+--------+--------+-----------+--------+--------+
18 # | /           |     64 |        |        |           |    216 |        |
19 # | cpu         |   5065 |   1345 |     12 |         4 |        |        |
20 # | dma0        |    654 |    254 |      1 |           |        |        |
21 # | dvga        |    816 |    755 |      4 |           |        |        |
22 # | eth3        |   2995 |   2337 |      4 |           |        |        |
23 # | jpg0        |   1682 |    681 |      2 |        13 |        |        |
24 # | leela       |    684 |    552 |      4 |         2 |        |        |
25 # | pia         |      9 |        |        |           |        |        |
26 # | pkmc_mc     |    219 |    122 |        |           |        |        |
27 # | rom0        |    111 |      3 |     12 |           |        |        |
28 # | sys_sig_gen |        |      6 |        |           |        |      2 |
29 # | uart2       |    824 |    346 |        |           |        |        |
30 # | wb_conbus   |    618 |     10 |        |           |        |        |
31 # +-------------+--------+--------+--------+-----------+--------+--------+
32 # | Total       |  13741 |   6411 |     39 |        19 |    216 |      2 |
33 # +-------------+--------+--------+--------+-----------+--------+--------+
34 #
35 # Example 2 (showing off hierarchical view of the same design)
36 # perl xdlanalyze.pl dafk.xdl 1
37 # XDLAnalyze V1.1 by Andreas Ehliar <ehliar@isy.liu.se>
38 # Analyzing the file dafk.xdl...................................................
39 # +-----------------------------+--------+--------+--------+-----------+--------+--------+
40 # | Module                      |   LUTS |     FF | RAMB16 | MULT18x18 |    IOB |    DCM |
41 # +-----------------------------+--------+--------+--------+-----------+--------+--------+
42 # | /                           |     64 |        |        |           |    216 |        |
43 # | cpu                         |      1 |        |        |           |        |        |
44 # | cpu/dwb_biu                 |     10 |     72 |        |           |        |        |
45 # | cpu/iwb_biu                 |     64 |     73 |        |           |        |        |
46 # | cpu/or1200_cpu              |   4441 |    987 |      2 |         4 |        |        |
47 # | cpu/or1200_dc_top           |    208 |     40 |      5 |           |        |        |
48 # | cpu/or1200_ic_top           |    182 |     38 |      5 |           |        |        |
49 # | cpu/or1200_immu_top         |     11 |     33 |        |           |        |        |
50 # | cpu/or1200_pic              |     32 |     38 |        |           |        |        |
51 # | cpu/or1200_tt               |    116 |     64 |        |           |        |        |
52 # | dma0                        |    617 |    235 |        |           |        |        |
53 # | dma0/fifo                   |     37 |     19 |      1 |           |        |        |
54 # | dvga                        |      5 |        |        |           |        |        |
55 # | dvga/regs                   |    279 |    360 |      3 |           |        |        |
56 # | dvga/rend                   |    174 |    110 |      1 |           |        |        |
57 # | dvga/spr                    |    358 |    285 |        |           |        |        |
58 # | eth3                        |     73 |     51 |        |           |        |        |
59 # | eth3/Mshreg_WillTransmit_q2 |      1 |        |        |           |        |        |
60 # | eth3/ethreg1                |    368 |    302 |        |           |        |        |
61 # | eth3/maccontrol1            |    295 |    103 |        |           |        |        |
62 # | eth3/macstatus1             |     60 |     18 |        |           |        |        |
63 # | eth3/miim1                  |    127 |     74 |        |           |        |        |
64 # | eth3/rxethmac1              |    311 |    107 |        |           |        |        |
65 # | eth3/txethmac1              |    369 |    119 |        |           |        |        |
66 # | eth3/wishbone               |   1391 |   1563 |      4 |           |        |        |
67 # | jpg0                        |    302 |     57 |      2 |           |        |        |
68 # | jpg0/dct0                   |    599 |    618 |        |        13 |        |        |
69 # | jpg0/tmem                   |    781 |      6 |        |           |        |        |
70 # | leela                       |     36 |        |        |           |        |        |
71 # | leela/cam0                  |    349 |    291 |      4 |         2 |        |        |
72 # | leela/mc0                   |    129 |     25 |        |           |        |        |
73 # | leela/regs0                 |    170 |    236 |        |           |        |        |
74 # | pia                         |      9 |        |        |           |        |        |
75 # | pkmc_mc/mem_fpga_board_if   |     40 |      1 |        |           |        |        |
76 # | pkmc_mc/pkmc_mc             |    179 |    121 |        |           |        |        |
77 # | rom0                        |     77 |      1 |        |           |        |        |
78 # | rom0/boot_prog_bram         |     34 |      2 |      8 |           |        |        |
79 # | rom0/boot_ram               |        |        |      4 |           |        |        |
80 # | sys_sig_gen                 |        |      6 |        |           |        |        |
81 # | sys_sig_gen/del0            |        |        |        |           |        |      1 |
82 # | sys_sig_gen/div0            |        |        |        |           |        |      1 |
83 # | uart2                       |      1 |        |        |           |        |        |
84 # | uart2/dbg                   |     33 |        |        |           |        |        |
85 # | uart2/regs                  |    725 |    266 |        |           |        |        |
86 # | uart2/wb_interface          |     65 |     80 |        |           |        |        |
87 # | wb_conbus                   |    618 |        |        |           |        |        |
88 # | wb_conbus/arb               |        |     10 |        |           |        |        |
89 # +-----------------------------+--------+--------+--------+-----------+--------+--------+
90 # | Total                       |  13741 |   6411 |     39 |        19 |    216 |      2 |
91 # +-----------------------------+--------+--------+--------+-----------+--------+--------+
92
93 # If you don't want to print all fields, change the @print_order
94 # declaration below.
95
96 # Note that the figures will usually not be exactly the same as the
97 # figures reported by map. This is partly because map does not count a 
98 # LUT with a constant output as a LUT, at least not in ISE 8.1.
99 #
100 # The program will also automatically convert a .ncd-file to a temporary
101 # .xdl file before running.
102 #
103 # This program has been tested on designs targetted at Virtex-2 and
104 # Virtex-4 from ISE 8.1 and ISE 8.2 on a Linux based computer. Note
105 # that it assumes that your path separator is set to /.
106 #
107 # Missing features:
108 #   * Slice count would be nice
109 #   * Show number of LUTs used as distributed RAM and SRL16
110 #   * Virtex-5 support
111 #
112 # NOTE:
113 #   The synthesizer will probably optimize your design across module boundaries
114 #   in some cases. This means that your figures cannot be entirely correct.
115
116 use strict;
117 use IO::Handle;
118 use IO::File;
119 use POSIX qw(tmpnam);
120
121
122 use constant {
123     LUTS => 1,
124     FF => 2,
125     IOB => 3,
126     RAMB16 => 4,
127     MULT_18X18 => 5,
128     DSP48 => 6,
129     DCM => 7,
130     DCM_ADV => 8,
131     BUFG => 9,
132     };
133
134 my @translation = ( "UNKNOWN", "LUTS","FF","IOB","RAMB16","MULT18x18",
135                     "DSP48", "DCM", "DCM_ADV", "BUFG" );
136
137
138 # Modify this line to include what you want printed and in what order
139 my @print_order = ( LUTS, FF, RAMB16, DSP48, MULT_18X18, IOB, DCM,
140                     DCM_ADV, BUFG );
141
142
143
144 ########################################################################
145 my %themodules;
146 my %hierarchical_usageinfo;
147 my %usageinfo;
148
149 # This variable controls how many levels we will keep track of
150 my $hierarchical_level = 0;
151
152 ######################################################################
153 # Add a component to the statistics
154 ######################################################################
155 sub add_component {
156     my $thename = $_[0];
157     my $thetype = $_[1];
158
159     my @temppath = split("/",$thename);
160     my @list = ();
161     my $currname = $temppath[0];
162     my $i;
163
164     push(@list,$currname);
165     
166     for($i = 1; $i < $#temppath; $i = $i  + 1) {
167         $currname = "$currname/$temppath[$i]";
168         push(@list,$currname);
169     }
170     
171     for($i = $hierarchical_level; $i < $#temppath; $i = $i + 1) {
172         $currname = pop(@list);
173         if($themodules{$currname}) {
174             $i = $#temppath;
175         }
176     }
177     
178
179     if($thename =~ /\//) {
180     }else{
181 # If there is no slash in the name => a component of the top level
182         $currname = "/";
183     }
184
185     my %thehash;
186
187     $hierarchical_usageinfo{$currname}{$thetype}++;
188     $usageinfo{$thetype}++;
189     
190     $themodules{$currname} = 1;
191 }
192
193 ######################################################################
194 # Analyze an XDL file and collect statistics about component
195 # usage
196 ######################################################################
197
198 sub analyze_file {
199
200     print "Analyzing the file $_[0]...";
201     STDOUT->autoflush(1);
202
203     open THEFILE, '<', $_[0] or die;
204
205     my $line = 0;
206     while (<THEFILE>){
207
208         # Progress meter
209         $line++;
210         if($line == 10000){
211             $line = 0;
212             print(".");
213             STDOUT->autoflush(1);
214         }
215
216         # Quick and dirty parser that does not keep any state.
217         # A real parser would remember what kind of instance we are
218         # currently inside in order to keep track of more things such
219         # as for example slice usage, etc.
220
221         # Both an F and a G LUT can be on the same line
222         # So we can't use elseif here.
223         if(/ F:([a-zA-Z0-9_\/\[\]<>\.]+:\#([A-Z]+):D=)/) {
224             &add_component($1,LUTS);
225         }
226
227         if(/ G:([a-zA-Z0-9_\/\[\]<>\.]+:\#([A-Z]+):D=)/) {
228             &add_component($1,LUTS);
229         }
230
231
232         # Both an FFX and a FFY flip flop can be on the same line
233         # So we can't use elseif here.
234         if(/FFX:([a-zA-Z0-9_\/\[\]<>\.]+:\#[A-Z]+)/) {
235             &add_component($1,FF);
236         }
237
238         if(/FFY:([a-zA-Z0-9_\/\[\]<>\.]+:\#[A-Z]+)/) {
239             &add_component($1,FF);
240         }
241
242         # Check if there is an instance defined on this line
243         if(/^inst/) {
244             if(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"IOB\"/){
245                 &add_component($1,IOB);
246             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"RAMB16\"/){
247                 &add_component($1,RAMB16);
248             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"MULT18X18\"/){
249                 &add_component($1,MULT_18X18);
250             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"DSP48\"/){
251                 &add_component($1,DSP48);
252             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"DCM\"/){
253                 # FIXME - Do we need a check for a dummy DCM?
254                 &add_component($1,DCM);
255             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"DCM_ADV\"/){
256                 # Check for unused dummy instantiation of DCM_ADV
257                 if(! /^inst \"XIL_ML_UNUSED_DCM/){
258                     &add_component($1,DCM_ADV);
259                 }
260             }elsif(/^inst \"([a-zA-Z0-9_\/\[\]<>\.]+)\" \"BUFG\"/){
261                 &add_component($1,BUFG);
262             }
263             # Adding more components here should be easy
264         }
265
266     }
267
268     print "\n";
269
270 }
271
272 ######################################################################
273 # Print a line consisting of dashes and plus like the marked lines in
274 # the following output:
275 # --> +-------------------+--------+--------+--------+
276 #     | Module            |   LUTS |     FF |    IOB |
277 # --> +-------------------+--------+--------+--------+
278 ######################################################################
279
280 sub print_dashes {
281     my $firstlen = $_[0];
282     my $inst_order = $_[1];
283     my $insttype;
284     print "+" . "-" x ($firstlen + 2);
285     foreach $insttype (@$inst_order) {
286         my $maxlen = length($translation[$insttype]);
287         $maxlen = $maxlen < 6 ? 6 : $maxlen;
288         print "+" . "-" x ($maxlen + 2);
289     }
290     printf("+\n");
291     
292 }
293
294 ######################################################################
295 # Print statistics collected in the %themodules, %usageinfo, and
296 # %hierarchical_usageinfo hashes.
297 ######################################################################
298
299 sub print_stats {
300
301     my $firstlen;
302     my @thekeys = keys %themodules;
303     @thekeys = sort(@thekeys);
304     my $i;
305     my $name;
306     my $foo;
307     my @inst_order;
308     
309 # Find maximum length of the first field
310     $firstlen = length("Module");
311     foreach $i (@thekeys) {
312         if($firstlen < length($i)){
313             $firstlen = length($i);
314         }
315     }
316     
317     my $insttype;
318     my $maxlen;
319
320 # Create inst_order so that we only print statistics about components
321 # that are actually used in the design.
322     foreach $name (@print_order) {
323         if($usageinfo{$name}) {
324             push(@inst_order,$name);
325         }
326     }
327
328 # Print header
329     &print_dashes($firstlen,\@inst_order);
330     printf("| %- ${firstlen}s ","Module");
331     foreach $insttype (@inst_order) {
332         printf("| % 6s ", $translation[$insttype]);
333     }
334     printf("|\n");
335     &print_dashes($firstlen,\@inst_order);
336
337
338 # Print hierarchical statistics
339     foreach $name (@thekeys) {
340         printf("| %- ${firstlen}s ",$name);
341         foreach $insttype (@inst_order) {
342             $maxlen = length($translation[$insttype]);
343             $maxlen = $maxlen < 6 ? 6 : $maxlen;
344             if($hierarchical_usageinfo{$name}{$insttype}) {
345                 printf("| % ${maxlen}d ",$hierarchical_usageinfo{$name}{$insttype});
346             }else {
347                 printf("| % ${maxlen}s "," ");
348             }
349         }
350         printf("|\n");
351     }
352
353 # Print footer with total number of all components
354     &print_dashes($firstlen,\@inst_order);
355
356     printf("| %- ${firstlen}s ","Total");
357     foreach $insttype (@inst_order) {
358         $maxlen = length($translation[$insttype]);
359         $maxlen = $maxlen < 6 ? 6 : $maxlen;
360         if($usageinfo{$insttype}){
361             printf("| % ${maxlen}d ",$usageinfo{$insttype});
362         }else{
363             printf("| % ${maxlen}s "," ");
364         }
365     }
366     printf("|\n");
367
368     &print_dashes($firstlen,\@inst_order);
369 }
370
371
372 ######################################################################
373 # Main program starts here
374 ######################################################################
375
376 printf('XDLAnalyze V1.1 by Andreas Ehliar <ehliar@isy.liu.se>'. "\n");
377
378 if(! $ARGV[0]){
379     print STDERR "Usage: xdlanalyze.pl <design.xdl> [hierarchical levels]\n";
380     exit 1;
381 }
382
383 if($ARGV[1]) {
384     $hierarchical_level = $ARGV[1];
385 }
386
387 # If the input has a .ncd file extension it will be converted to an .xdl file
388 if( $ARGV[0] =~ /.ncd$/ ) {
389     my $tempname;
390     my $fh;
391
392     printf("Calling xdl -ncd2xdl to convert .ncd file to .xdl file before running analyzer\n");
393
394     # try new temporary filenames until we get one that didn't already exist as per
395     # the perl cookbook
396     do { $tempname = tmpnam() . ".xdl" }
397         until $fh = IO::File->new($tempname, O_RDWR|O_CREAT|O_EXCL);
398     
399     # Call the xdl tool to convert the file...
400     system ("xdl", "-ncd2xdl", $ARGV[0], $tempname) == 0 or die "Couldn't call xdl: $!";
401     &analyze_file($tempname);
402
403     unlink($tempname) or die "Couldn't unlink $tempname : $!";
404     
405 }else {
406     &analyze_file($ARGV[0]);
407 }
408
409
410 &print_stats;
411
412 exit 0;