File Coverage

blib/lib/Verilog/VCD.pm
Criterion Covered Total %
statement 152 152 100.0
branch 78 78 100.0
condition 12 12 100.0
subroutine 16 16 100.0
pod 10 13 76.9
total 268 271 98.8


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