File Coverage

blib/lib/Verilog/VCD.pm
Criterion Covered Total %
statement 108 114 94.7
branch 50 54 92.5
condition 9 9 100.0
subroutine 8 8 100.0
pod 4 5 80.0
total 179 190 94.2


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