File Coverage

blib/lib/ModelSim/List.pm
Criterion Covered Total %
statement 125 140 89.2
branch 49 60 81.6
condition 13 15 86.6
subroutine 11 12 91.6
pod 5 5 100.0
total 203 232 87.5


line stmt bran cond sub pod time code
1             package ModelSim::List;
2              
3 2     2   92453 use strict;
  2         6  
  2         77  
4 2     2   11 use warnings;
  2         4  
  2         9333  
5              
6             our $VERSION = '0.06';
7             our $error;
8              
9             sub new ($) {
10 2 50   2 1 64 my $class = ref $_[0] ? ref shift : shift;
11 2         8 return bless {}, $class;
12             }
13              
14             sub parse ($$) {
15 5     5 1 842 my ($self, $file) = @_;
16 5         10 my ($ast, $fh);
17 5 100       317 unless(open $fh, $file) {
18 1         14 $error = "file error - Can't open $file for reading: $!\n";
19 1         5 return undef;
20             }
21 4         123 $_ = <$fh>;
22 4 100       23 if (/^\@/o) {
23 3         15 $ast = _parse_event_list($fh, $file);
24             } else {
25 1         6 $ast = _parse_normal_list($fh, $file);
26             }
27 4         56 close $fh;
28 4 50       17 return undef unless $ast;
29 4         54 %$self = %$ast;
30 4         109 return 1;
31             }
32              
33             sub _parse_normal_list ($$) {
34 1     1   4 my ($fh, $file) = @_;
35 1         2 my $ast = {};
36 1         2 my (%cols, @cols);
37 1         3 my $state = 0;
38 1         2 do {{
39 17 50       19 next if /^\s*$/o;
  17         58  
40 17 100       37 if ($state == 0) {
    50          
41 4 100       41 if (/\b\/\w+|\bdelta\b|\bns\b/o) {
42             # process signal name list:
43 3         17 while (/[\w\/]+/go) {
44 6         55 $cols{$+[0]} = $&;
45             #warn "$& - ", $+[0], "\n";
46             }
47             } else {
48             # process the first data line:
49 1         7 @cols = _sort_cols(%cols);
50 1         5 undef %cols;
51 1         5 _parse_data_line($ast, @cols);
52 1         5 $state = 1;
53             }
54             } elsif ($state == 1) {
55             # process data line:
56 13         23 _parse_data_line($ast, @cols);
57             } else {
58 0         0 chomp;
59 0         0 $error = "file error - Syntax error: line $.: $_\n";
60 0         0 return undef;
61             }
62             }} while (<$fh>);
63             #die Data::Dumper->Dump([$ast], [qw(self)]);
64 1         5 return $ast;
65             }
66              
67             # used in sub _parse_normal_list:
68             sub _parse_data_line {
69 14     14   39 my ($ast, @cols) = @_;
70 14         52 s/^\s+//;
71 14         53 my @data = split(/\s+/, $_);
72 14         21 my $time = $data[0];
73 14         43 for(my $i = 1; $i < @data; $i++) {
74 70         85 my $sig = $cols[$i];
75 70         77 my $val = $data[$i];
76             #warn "@cols", " -> ", $sig, "\n";
77             #warn "@data", " -> ", $val, "\n";
78 70 50       123 next if $sig eq 'delta';
79 70 100       193 $ast->{$sig} = [] unless ref $ast->{$sig};
80 70 100 100     62 if (@{$ast->{$sig}} and ${$ast->{$sig}}[-2] eq $val) {
  70         160  
  66         320  
81 44         244 next;
82             }
83 26 50 66     27 if (@{$ast->{$sig}} and ${$ast->{$sig}}[-1] eq $time) {
  26         64  
  22         70  
84 0         0 ${$ast->{$sig}}[-2] = $val;
  0         0  
85 0         0 next;
86             }
87 26         28 push @{$ast->{$sig}}, $val, $time;
  26         142  
88             }
89             }
90              
91             # used in sub _parse_normal_list:
92             sub _sort_cols {
93 1     1   6 my %cols = @_;
94 1         2 my @cols;
95 1         8 foreach my $colnum (sort keys %cols) {
96 6         54 push @cols, $cols{$colnum};
97             }
98 1         9 return @cols;
99             }
100              
101             sub _parse_event_list {
102 3     3   7 my ($fh, $file) = @_;
103 3         7 my $ast = {};
104 3         4 my $time;
105 3         7 do {{
106 315 50       312 next if /^\s*$/;
  315         821  
107 315 100       791 if (/^\@(\d+)/) {
108 144         208 $time = $1;
109 144         1043 next;
110             }
111 171 50       322 unless (defined $time) {
112 0         0 $error = "file error - $file: Invalid file format\n";
113 0         0 return undef;
114             }
115 171 50       658 if (/^([\w\/]+)\s+(\w+)\s*$/) {
116 171         680 my ($sig, $val) = ($1, $2);
117 171 100       471 $ast->{$sig} = [] unless ref $ast->{$sig};
118 171 100 100     167 if (@{$ast->{$sig}} and ${$ast->{$sig}}[-1] eq $time) {
  171         470  
  163         574  
119 17         21 ${$ast->{$sig}}[-2] = $val;
  17         27  
120 17         70 next;
121             }
122 154         188 push @{$ast->{$sig}}, $val, $time;
  154         540  
123 154         959 next;
124             }
125 0         0 $error = "file error - $file: Invalid file format\n";
126 0         0 return undef;
127             }} while (<$fh>);
128             #die Data::Dumper->Dump([$ast], [qw(self)]);
129 3         12 return $ast;
130             }
131              
132             sub strobe {
133 280     280 1 559 my ($self, $signal, $time) = @_;
134 280 50       744 unless ($self->{$signal}) {
135 0         0 $error = "strobe error - $signal: No such signal name.\n";
136 0         0 return undef;
137             }
138 280         316 my @events = @{$self->{$signal}};
  280         3069  
139 280 100       797 if ($time < $events[1]) {
140 6         48 $error = "strobe error - $signal: time $time underflow.\n";
141 6         42 return undef;
142             }
143 274         299 my $value;
144 274         544 while (@events) {
145 3373         4310 my $val = shift @events;
146 3373         4290 my $tm = shift @events;
147              
148 3373 100       5777 if ($time < $tm) {
149 265         1703 return $value;
150             } else {
151 3108         6399 $value = $val;
152             }
153             }
154 9         43 return $value;
155             }
156              
157             sub _val_eq {
158 365     365   550 my ($val, $pat) = @_;
159 365 100       1599 return $val =~ $pat if (ref($pat) eq 'Regexp');
160 223         742 return $val eq $pat;
161             }
162              
163             sub time_of {
164 159     159 1 408 my ($self, $signal, $value, $start, $end) = @_;
165 159 100       382 $start = 0 unless defined $start;
166 159 100 66     751 if (defined $start and defined $end) {
167 127 100       368 if ($start > $end) {
168 1         5 $error = "time_of error - Starting time $start greater than the ".
169             "ending time $end\n";
170 1         6 return undef;
171             }
172             }
173 158 50       397 unless ($self->{$signal}) {
174 0         0 $error = "time_of error - $signal: No such signal name.\n";
175 0         0 return undef;
176             }
177 158         536 my @events = @{$self->{$signal}};
  158         2102  
178 158         222 my $time;
179 158 100       359 return $start if _val_eq($self->strobe($signal, $start), $value);
180 143         1100 while (@events) {
181 2239         3229 my $val = shift @events;
182 2239         2758 my $tm = shift @events;
183              
184 2239 100       4187 if ($tm < $start) {
185 2032         4345 next;
186             }
187 207 100       345 if (_val_eq $val, $value) {
188 136 100 100     550 if (defined $end and $tm > $end) {
189 5         18 $error = "time_of warning - $signal: Never achieved value $value ".
190             "during the time interval specified.\n";
191 5         35 return undef;
192             }
193 131         954 return $tm;
194             }
195             }
196 7         37 $error = "time_of warning - $signal: Never achieved value $value ".
197             "during the time interval specified.\n";
198 7         42 return undef;
199             }
200              
201             # Return the error info:
202             sub error {
203 0     0 1   return $error;
204             }
205              
206             1;
207             __END__