File Coverage

blib/lib/Verilog/VCD.pm
Criterion Covered Total %
statement 143 144 99.3
branch 72 72 100.0
condition 9 9 100.0
subroutine 14 14 100.0
pod 9 11 81.8
total 247 250 98.8


line stmt bran cond sub pod time code
1             package Verilog::VCD;
2              
3 5     5   101119 use warnings;
  5         11  
  5         203  
4 5     5   27 use strict;
  5         8  
  5         132  
5 5     5   23 use Carp qw(croak);
  5         13  
  5         10326  
6              
7             require Exporter;
8             our @ISA = qw(Exporter);
9             our @EXPORT_OK = qw(
10             parse_vcd list_sigs get_timescale get_endtime
11             get_date get_version get_dumps
12             get_decl_comments get_sim_comments
13             );
14             our %EXPORT_TAGS = (all => \@EXPORT_OK);
15              
16             our $VERSION = '0.07';
17              
18             my $timescale;
19             my $endtime;
20             my $date;
21             my $version;
22             my @decl_comments;
23             my @sim_comments;
24             my %dumps;
25              
26             sub list_sigs {
27             # Parse input VCD file into data structure,
28             # then return just a list of the signal names.
29 5     5 1 1265 my $file = shift;
30 5 100       19 unless (defined $file) {
31 2         443 croak('Error: list_sigs requires a filename. It seems like no ',
32             'filename was provided or filename was undefined');
33             }
34 3         13 my $vcd = parse_vcd($file, {only_sigs => 1});
35              
36 2         3 my @sigs;
37 2         3 for my $code (keys %{ $vcd }) {
  2         10  
38 14         11 my @nets = @{ $vcd->{$code}->{nets} };
  14         32  
39 14         16 push @sigs, map { "$_->{hier}.$_->{name}" } @nets;
  17         54  
40             }
41 2         33 return @sigs;
42             }
43              
44             sub parse_vcd {
45             # Parse input VCD file into data structure.
46             # Also, print t-v pairs to STDOUT, if requested.
47 29     29 1 15005 my ($file, $opt) = @_;
48              
49 29 100       80 unless (defined $file) {
50 2         215 croak('Error: parse_vcd requires a filename. It seems like no ',
51             'filename was provided or filename was undefined');
52             }
53              
54 27 100       62 if ($opt) {
55 22 100       68 unless (ref($opt) eq 'HASH') {
56 3         426 croak('Error: If options are passed to parse_vcd, they must be ',
57             'passed as a hash reference.');
58             }
59             }
60              
61 24 100       59 my $only_sigs = (exists $opt->{only_sigs}) ? 1 : 0;
62              
63 24         23 my $all_sigs;
64             my %usigs;
65 24 100       63 if (exists $opt->{siglist}) {
66 4         6 %usigs = map { $_ => 1 } @{ $opt->{siglist} };
  4         13  
  4         11  
67 4 100       19 unless (%usigs) {
68 1         151 croak('Error: The signal list passed using siglist was empty.');
69             }
70 3         4 $all_sigs = 0;
71             }
72             else {
73 20         28 $all_sigs = 1;
74             }
75              
76 23 100       39 my $use_stdout = (exists $opt->{use_stdout}) ? 1 : 0;
77              
78 23 100       1086 open my $fh, '<', $file or croak("Error: Can not open VCD file $file: $!");
79              
80             # Parse declaration section of VCD file
81 21         28 my %data;
82             my $mult;
83 0         0 my @hier;
84 21         44 @decl_comments = ();
85 21         307 while (<$fh>) {
86 565 100       2023 if (s/ ^ \s* \$ (\w+) \s+ //x) {
87 330         474 my $keyword = $1;
88 330         265 my $line = $_;
89 330         258 my @lines;
90 330 100       547 push @lines, $_ if length $_;
91 330         820 while ($line !~ / \$end \b /x) {
92 126         189 $line = <$fh>;
93 126         393 push @lines, $line;
94             }
95 330         373 for my $line (@lines) {
96 394         510 trim($line);
97 394         1055 $line =~ s/ \s* \$end \b //x;
98             }
99 330 100       837 pop @lines unless length $lines[-1];
100              
101 330 100       1123 if ($keyword eq 'date') {
    100          
    100          
    100          
    100          
    100          
    100          
102 20         94 $date = join "\n", @lines;
103             }
104             elsif ($keyword eq 'version') {
105 20         172 $version = join "\n", @lines;
106             }
107             elsif ($keyword eq 'comment') {
108 18         114 push @decl_comments, join "\n", @lines;
109             }
110             elsif ($keyword eq 'timescale') {
111 20         70 $mult = calc_mult("@lines", $opt);
112             }
113             elsif ($keyword eq 'scope') {
114 78         103 my $scope = "@lines";
115 78         392 push @hier, (split /\s+/, $scope)[1]; # just keep scope name
116             }
117             elsif ($keyword eq 'upscope') {
118 78         198 pop @hier;
119             }
120             elsif ($keyword eq 'var') {
121 79         121 my $var = "@lines";
122             # $var reg 1 *@ data $end
123             # $var wire 4 ) addr [3:0] $end
124 79         272 my ($type, $size, $code, $name) = split /\s+/, $var, 4;
125 79         124 $name =~ s/ \s //xg;
126 79         142 my $path = join '.', @hier;
127 79         101 my $full_name = "$path.$name";
128 49         404 push @{ $data{$code}{nets} }, {
129             type => $type,
130             name => $name,
131             size => $size,
132             hier => $path,
133 79 100 100     364 } if exists $usigs{$full_name} or $all_sigs;
134             }
135             else { # enddefinitions
136 17         29 last;
137             }
138             }
139             }
140              
141 18         33 my $num_sigs = scalar keys %data;
142 18 100       36 unless ($num_sigs) {
143 2 100       8 if ($all_sigs) {
144 1         142 croak("Error: No signals were found in the VCD file $file.",
145             'Check the VCD file for proper $var syntax.');
146             }
147             else {
148 1         212 croak("Error: No matching signals were found in the VCD file $file.",
149             ' Use list_sigs to view all signals in the VCD file.');
150             }
151             }
152 16 100 100     58 if (($num_sigs>1) and $use_stdout) {
153 2         215 croak("Error: There are too many signals ($num_sigs) for output ",
154             'to STDOUT. Use list_sigs to select a single signal.');
155             }
156              
157 14 100       29 unless ($only_sigs) {
158             # Parse simulation section of VCD file
159             # Continue reading file
160 12         19 @sim_comments = ();
161 12         21 %dumps = ();
162 12         17 my $time = 0;
163 12         81 while (<$fh>) {
164 299         367 trim($_);
165 299 100 100     1187 if (s/ ^ \$ comment \s* //x) {
    100          
    100          
    100          
166 2         4 my $line = $_;
167 2         9 my @lines;
168 2 100       10 push @lines, $_ if length $_;
169 2         10 while ($line !~ / \$end \b /x) {
170 3         5 $line = <$fh>;
171 3         8 push @lines, $line;
172             }
173 2         4 for my $line (@lines) {
174 4         7 trim($line);
175 4         12 $line =~ s/ \s* \$end \b //x;
176             }
177 2 100       6 pop @lines unless length $lines[-1];
178 2         5 my $comment = join "\n", @lines;
179 2         12 push @sim_comments, {time => $time, comment => $comment};
180             }
181             elsif (/ ^ \$ (dump \w+) /x) {
182 17         16 push @{ $dumps{$1} }, $time;
  17         76  
183             }
184             elsif (/ ^ [#] (\d+) /x) {
185 112         349 $time = $mult * $1;
186             }
187             elsif (/ ^ ([01zx]) (.+) /xi or / ^ [br] (\S+) \s+ (.+) /xi) {
188 150         247 my $value = lc $1;
189 150         148 my $code = $2;
190 150 100       281 if (exists $data{$code}) {
191 114 100       137 if ($use_stdout) {
192 20         101 print "$time $value\n";
193             }
194             else {
195 94         64 push @{ $data{$code}{tv} }, [$time, $value];
  94         427  
196             }
197             }
198             }
199             }
200 12         20 $endtime = $time;
201             }
202              
203 14         128 close $fh;
204              
205 14         81 return \%data;
206             }
207              
208             sub calc_mult {
209             # Calculate a new multiplier for time values.
210             # Return numeric multiplier.
211             # Also sets the package $timescale variable.
212              
213 20     20 0 31 my ($tscale, $opt) = @_;
214              
215 20         22 my $new_units;
216 20 100       45 if (exists $opt->{timescale}) {
217 10         20 $new_units = lc $opt->{timescale};
218 10         17 $new_units =~ s/\s//g;
219 10         46 $timescale = "1$new_units";
220             }
221             else {
222 10         28 $timescale = $tscale;
223 10         48 return 1;
224             }
225              
226 10         7 my $mult;
227             my $units;
228 10 100       41 if ($tscale =~ / (\d+) \s* ([a-z]+) /xi) {
229 9         17 $mult = $1;
230 9         16 $units = lc $2;
231             }
232             else {
233 1         160 croak("Error: Unsupported timescale found in VCD file: $tscale. ",
234             'Refer to the Verilog LRM.');
235             }
236              
237 9         49 my %mults = (
238             'fs' => 1e-15,
239             'ps' => 1e-12,
240             'ns' => 1e-09,
241             'us' => 1e-06,
242             'ms' => 1e-03,
243             's' => 1e-00,
244             );
245 9         45 my $usage = join '|', sort { $mults{$a} <=> $mults{$b} } keys %mults;
  95         119  
246              
247 9         12 my $scale;
248 9 100       17 if (exists $mults{$units}) {
249 8         11 $scale = $mults{$units};
250             }
251             else {
252 1         161 croak("Error: Unsupported timescale units found in VCD file: $units. ",
253             "Supported values are: $usage");
254             }
255              
256 8         7 my $new_scale;
257 8 100       12 if (exists $mults{$new_units}) {
258 7         10 $new_scale = $mults{$new_units};
259             }
260             else {
261 1         186 croak("Error: Illegal user-supplied timescale: $new_units. ",
262             "Legal values are: $usage");
263             }
264              
265 7         52 return (($mult * $scale) / $new_scale);
266             }
267              
268             sub trim {
269             # Modify input string in-place
270 697     697 0 1933 $_[0] =~ s/ \s+ \z //x; # Remove trailing whitespace
271 697         1062 $_[0] =~ s/ ^ \s+ //x; # Remove leading whitespace
272             }
273              
274             sub get_timescale {
275 7     7 1 3676 return $timescale;
276             }
277              
278             sub get_endtime {
279 6     6 1 4315 return $endtime;
280             }
281              
282             sub get_date {
283 1     1 1 3 return $date;
284             }
285              
286             sub get_version {
287 1     1 1 3 return $version;
288             }
289              
290             sub get_decl_comments {
291 4     4 1 4026 return @decl_comments;
292             }
293              
294             sub get_sim_comments {
295 2     2 1 879 return @sim_comments;
296             }
297              
298             sub get_dumps {
299 4     4 1 1033 return %dumps;
300             }
301              
302              
303             =head1 NAME
304              
305             Verilog::VCD - Parse a Verilog VCD text file
306              
307             =head1 VERSION
308              
309             This document refers to Verilog::VCD version 0.07.
310              
311             =head1 SYNOPSIS
312              
313             use Verilog::VCD qw(parse_vcd);
314             my $vcd = parse_vcd('/path/to/some.vcd');
315              
316             =head1 DESCRIPTION
317              
318             Verilog is a Hardware Description Language (HDL) used to model digital logic.
319             While simulating logic circuits, the values of signals can be written out to
320             a Value Change Dump (VCD) file. This module can be used to parse a VCD file
321             so that further analysis can be performed on the simulation data. The entire
322             VCD file can be stored in a Perl data structure and manipulated using
323             standard hash and array operations.
324              
325             =head2 Input File Syntax
326              
327             The syntax of the VCD text file is described in the documentation of
328             the IEEE standard for Verilog. Only the four-state VCD format is supported.
329             The extended VCD format (with strength information) is not supported.
330             Since the input file is assumed to be legal VCD syntax, only minimal
331             validation is performed.
332              
333             =head1 SUBROUTINES
334              
335              
336             =head2 parse_vcd($file, $opt_ref)
337              
338             Parse a VCD file and return a reference to a data structure which
339             includes hierarchical signal definitions and time-value data for all
340             the specified signals. A file name is required. By default, all
341             signals in the VCD file are included, and times are in units
342             specified by the C<$timescale> VCD keyword.
343              
344             my $vcd = parse_vcd('/path/to/some.vcd');
345              
346             It returns a reference to a nested data structure. The top of the
347             structure is a Hash-of-Hashes. The keys to the top hash are the VCD
348             identifier codes for each signal. The following is an example
349             representation of a very simple VCD file. It shows one signal named
350             C, whose VCD code is C<+>. The time-value pairs
351             are stored as an Array-of-Arrays, referenced by the C key. The
352             time is always the first number in the pair, and the times are stored in
353             increasing order in the array.
354              
355             {
356             '+' => {
357             'tv' => [
358             [
359             '0',
360             '1'
361             ],
362             [
363             '12',
364             '0'
365             ],
366             ],
367             'nets' => [
368             {
369             'hier' => 'chip.cpu.alu.',
370             'name' => 'clk',
371             'type' => 'reg',
372             'size' => '1'
373             }
374             ]
375             }
376             };
377              
378             Since each code could have multiple hierarchical signal names, the names are
379             stored as an Array-of-Hashes, referenced by the C key. The example above
380             only shows one signal name for the code.
381              
382              
383             =head3 OPTIONS
384              
385             Options to C should be passed as a hash reference.
386              
387             =over 4
388              
389             =item timescale
390              
391             It is possible to scale all times in the VCD file to a desired timescale.
392             To specify a certain timescale, such as nanoseconds:
393              
394             my $vcd = parse_vcd($file, {timescale => 'ns'});
395              
396             Valid timescales are:
397              
398             s ms us ns ps fs
399              
400             =item siglist
401              
402             If only a subset of the signals included in the VCD file are needed,
403             they can be specified by a signal list passed as an array reference.
404             The signals should be full hierarchical paths separated by the dot
405             character. For example:
406              
407             my @signals = qw(
408             top.chip.clk
409             top.chip.cpu.alu.status
410             top.chip.cpu.alu.sum[15:0]
411             );
412             my $vcd = parse_vcd($file, {siglist => \@signals});
413              
414             Limiting the number of signals can substantially reduce memory usage of the
415             returned data structure because only the time-value data for the selected
416             signals is loaded into the data structure.
417              
418             =item use_stdout
419              
420             It is possible to print time-value pairs directly to STDOUT for a
421             single signal using the C option. If the VCD file has
422             more than one signal, the C option must also be used, and there
423             must only be one signal specified. For example:
424              
425             my $vcd = parse_vcd($file, {
426             use_stdout => 1,
427             siglist => [(top.clk)]
428             });
429              
430             The time-value pairs are output as space-separated tokens, one per line.
431             For example:
432              
433             0 x
434             15 0
435             277 1
436             500 0
437              
438             Times are listed in the first column.
439             Times units can be controlled by the C option.
440              
441             =item only_sigs
442              
443             Parse a VCD file and return a reference to a data structure which
444             includes only the hierarchical signal definitions. Parsing stops once
445             all signals have been found. Therefore, no time-value data are
446             included in the returned data structure. This is useful for
447             analyzing signals and hierarchies.
448              
449             my $vcd = parse_vcd($file, {only_sigs => 1});
450              
451             =back
452              
453              
454             =head2 list_sigs($file)
455              
456             Parse a VCD file and return a list of all signals in the VCD file.
457             Parsing stops once all signals have been found. This is
458             helpful for deciding how to limit what signals are parsed.
459              
460             Here is an example:
461              
462             my @signals = list_sigs('input.vcd');
463              
464             The signals are full hierarchical paths separated by the dot character
465              
466             top.chip.cpu.alu.status
467             top.chip.cpu.alu.sum[15:0]
468              
469             =head2 get_timescale( )
470              
471             This returns a string corresponding to the timescale as specified
472             by the C<$timescale> VCD keyword. It returns the timescale for
473             the last VCD file parsed. If called before a file is parsed, it
474             returns an undefined value. If the C C option
475             was used to specify a timescale, the specified value will be returned
476             instead of what is in the VCD file.
477              
478             my $vcd = parse_vcd($file); # Parse a file first
479             my $ts = get_timescale(); # Then query the timescale
480              
481             =head2 get_endtime( )
482              
483             This returns the last time found in the VCD file, scaled
484             appropriately. It returns the last time for the last VCD file parsed.
485             If called before a file is parsed, it returns an undefined value.
486              
487             my $vcd = parse_vcd($file); # Parse a file first
488             my $et = get_endtime(); # Then query the endtime
489              
490             =head2 get_date( )
491              
492             This returns a string corresponding to the date as specified
493             by the C<$date> VCD keyword. It returns the date for
494             the last VCD file parsed. If called before a file is parsed, it
495             returns an undefined value.
496              
497             my $vcd = parse_vcd($file); # Parse a file first
498             my $date = get_date(); # Then query the date
499              
500             =head2 get_version( )
501              
502             This returns a string corresponding to the version as specified
503             by the C<$version> VCD keyword. It returns the version for
504             the last VCD file parsed. If called before a file is parsed, it
505             returns an undefined value.
506              
507             my $vcd = parse_vcd($file); # Parse a file first
508             my $version = get_version(); # Then query the version
509              
510             =head2 get_decl_comments( )
511              
512             This returns an array corresponding to the comments as specified by the
513             C<$comment> VCD keyword in the declaration section of the VCD file. There
514             may be any number of C<$comment> keywords in the file. It returns the
515             comments for the last VCD file parsed. If called before a file is parsed,
516             it returns an empty array. If there are no C<$comment> keywords in the
517             declaration section of the file, it returns an empty array.
518              
519             my $vcd = parse_vcd($file); # Parse a file first
520             my @comms = get_decl_comments(); # Then query the comments
521              
522             =head2 get_sim_comments( )
523              
524             This returns an array-of-hashes corresponding to the comments as specified
525             by the C<$comment> VCD keyword in the simulation section of the VCD file.
526             There may be any number of C<$comment> keywords in the file. It returns
527             the comments for the last VCD file parsed. If called before a file is
528             parsed, it returns an empty array. If there are no C<$comment> keywords in
529             the declaration section of the file, it returns an empty array.
530              
531             my $vcd = parse_vcd($file); # Parse a file first
532             my @comms = get_sim_comments(); # Then query the comments
533              
534             The time at which the comment occurred is included with each comment.
535             An example returned structure is:
536              
537             ({time => 123, comment => '1st comment'},
538             {time => 456, comment => '2nd comment'})
539              
540             =head2 get_dumps( )
541              
542             This returns a hash-of-arrays corresponding to the C<$dump*> VCD keywords
543             (C<$dumpvars>, C<$dumpon>, etc.) in the simulation section of the VCD
544             file. The keys of the hash are the keywords, and the values are the
545             simulation times at which the keywords occurred. It returns the dump
546             commands for the last VCD file parsed. If called before a file is parsed,
547             it returns an empty hash.
548              
549             my $vcd = parse_vcd($file); # Parse a file first
550             my %dumps = get_dumps(); # Then query the dump commands
551              
552             =head1 EXPORT
553              
554             Nothing is exported by default. Functions may be exported individually, or
555             all functions may be exported at once, using the special tag C<:all>.
556              
557             =head1 DIAGNOSTICS
558              
559             Error conditions cause the program to die using C from the
560             L Core module.
561              
562             =head1 LIMITATIONS
563              
564             The extended VCD format (with strength information) is not supported.
565              
566             The default mode of C is to load the entire VCD file into the
567             data structure. This could be a problem for huge VCD files. The best solution
568             to any memory problem is to plan ahead and keep VCD files as small as possible.
569             When simulating, dump fewer signals and scopes, and use shorter dumping
570             time ranges. Another technique is to parse only a small list of signals
571             using the C option; this method only loads the desired signals into
572             the data structure. Finally, the C option will parse the input VCD
573             file line-by-line, instead of loading it into the data structure, and directly
574             prints time-value data to STDOUT. The drawback is that this only applies to
575             one signal.
576              
577             =head1 BUGS
578              
579             There are no known bugs in this module.
580              
581             =head1 SEE ALSO
582              
583             Refer to the following Verilog documentation:
584              
585             IEEE Standard for Verilog (c) Hardware Description Language
586             IEEE Std 1364-2005
587             Section 18.2, "Format of four-state VCD file"
588              
589             =head1 AUTHOR
590              
591             Gene Sullivan (gsullivan@cpan.org)
592              
593             =head1 COPYRIGHT AND LICENSE
594              
595             Copyright (c) 2012 Gene Sullivan. All rights reserved.
596              
597             This module is free software; you can redistribute it and/or modify
598             it under the same terms as Perl itself. See L.
599              
600             =cut
601              
602             1;
603