1 |
16 |
alirezamon |
###############################################################################
|
2 |
|
|
#
|
3 |
|
|
# File: rvp.pm
|
4 |
|
|
# RCS: $Header: /home/cc/v2html/build/../RCS/rvp.pm,v 7.61 2006/03/25 22:24:57 cc Exp $
|
5 |
|
|
# Description: The Rough Verilog Parser Perl Module
|
6 |
|
|
# Author: Costas Calamvokis
|
7 |
|
|
# Created: Fri Apr 10 16:59:30 1998
|
8 |
|
|
# Modified: Thu Jan 12 10:45:27 2006
|
9 |
|
|
# Language: Perl
|
10 |
|
|
#
|
11 |
|
|
# Copyright 1998-2006 Costas Calamvokis
|
12 |
|
|
#
|
13 |
|
|
# This file nay be copied, modified and distributed only in accordance
|
14 |
|
|
# with the terms of the limited licence contained in the accompanying
|
15 |
|
|
# file LICENCE.TXT.
|
16 |
|
|
#
|
17 |
|
|
###############################################################################
|
18 |
|
|
#
|
19 |
|
|
|
20 |
|
|
=head1 rvp - Rough Verilog Parser Perl Module
|
21 |
|
|
|
22 |
|
|
The basic idea is that first you call read_verilog will a list of all of your
|
23 |
|
|
files. The files are parsed and information stored away. You are then
|
24 |
|
|
handed back a pointer to the information which you can use in calls
|
25 |
|
|
to the various get_ function to get information about the verilog design.
|
26 |
|
|
|
27 |
|
|
For Example:
|
28 |
|
|
|
29 |
|
|
#!/usr/bin/perl -w
|
30 |
|
|
use rvp; # use the rough verilog parser
|
31 |
|
|
|
32 |
|
|
# Read in all the files specified on the command line
|
33 |
|
|
$vdata = rvp->read_verilog(\@ARGV,[],{},1,[],[],'');
|
34 |
|
|
|
35 |
|
|
# Print out all the modules found
|
36 |
|
|
foreach $module ($vdata->get_modules()) { print "$module\n"; }
|
37 |
|
|
|
38 |
|
|
Unless you are doing something very strange, you can probably ignore all
|
39 |
|
|
of the functions that have the words 'context' or 'anchors' in them!
|
40 |
|
|
|
41 |
|
|
=cut
|
42 |
|
|
|
43 |
|
|
package rvp;
|
44 |
|
|
|
45 |
|
|
#use strict;
|
46 |
|
|
#use Tie::IxHash;
|
47 |
|
|
|
48 |
|
|
use File::Basename;
|
49 |
|
|
|
50 |
|
|
use vars qw(@verilog_gatetype_keywords $verilog_gatetype_regexp
|
51 |
|
|
@verilog_compiler_keywords
|
52 |
|
|
@verilog_signal_keywords @verilog_sigs $verilog_sigs_regexp
|
53 |
|
|
$quiet $debug $VID $HVID $VNUM
|
54 |
|
|
$takenArcs
|
55 |
|
|
$baseEval $rvpEval $debugEval $languageDef $vid_vnum_or_string
|
56 |
|
|
$version $VERSION);
|
57 |
|
|
|
58 |
|
|
BEGIN {
|
59 |
|
|
# $VERSION is used by 'use', but keep $version for backwards compatibility
|
60 |
|
|
$version = '$Header: /home/cc/v2html/build/../RCS/rvp.pm,v 7.61 2006/03/25 22:24:57 cc Exp $'; #'
|
61 |
|
|
$version =~ s/^\S+ \S+ (\S+) .*$/$1/;
|
62 |
|
|
$VERSION = $version;
|
63 |
|
|
|
64 |
|
|
@verilog_signal_keywords = qw(input output inout
|
65 |
|
|
wire tri tri1 supply0 wand triand tri0
|
66 |
|
|
supply1 wor time trireg trior
|
67 |
|
|
reg integer real realtime
|
68 |
|
|
|
69 |
|
|
genvar
|
70 |
|
|
);
|
71 |
|
|
@verilog_sigs = @verilog_signal_keywords; # for backwards compatiblity
|
72 |
|
|
|
73 |
|
|
#V2001
|
74 |
|
|
|
75 |
|
|
$verilog_sigs_regexp = "\\b(?:" .
|
76 |
|
|
join("|",@verilog_signal_keywords) .
|
77 |
|
|
")\\b";
|
78 |
|
|
|
79 |
|
|
@verilog_gatetype_keywords = qw(and nand or nor xor xnor buf bufif0 bufif1
|
80 |
|
|
not notif0 notif1 pulldown pullup
|
81 |
|
|
nmos rnmos pmos rpmos cmos rcmos tran rtran
|
82 |
|
|
tranif0 rtranif0 tranif1 rtranif1
|
83 |
|
|
);
|
84 |
|
|
|
85 |
|
|
$verilog_gatetype_regexp = "\\b(?:" .
|
86 |
|
|
join("|",@verilog_gatetype_keywords) .
|
87 |
|
|
")\\b";
|
88 |
|
|
|
89 |
|
|
# Note: optimisation code in _search() assumes all of
|
90 |
|
|
# these compiler keywords contain a `
|
91 |
|
|
@verilog_compiler_keywords = qw(
|
92 |
|
|
`celldefine `define
|
93 |
|
|
`delay_mode_path `disable_portfaults
|
94 |
|
|
`else `enable_portfaults
|
95 |
|
|
`endcelldefine `endif
|
96 |
|
|
`ifdef `include
|
97 |
|
|
`nosuppress_faults `suppress_faults
|
98 |
|
|
`timescale `undef
|
99 |
|
|
`resetall `delay_mode_distributed
|
100 |
|
|
|
101 |
|
|
`default_nettype `file `line `ifndef `elsif
|
102 |
|
|
); #`
|
103 |
|
|
|
104 |
|
|
# a verilog identifier is this reg exp
|
105 |
|
|
# a non-escaped identifier is A-Z a-z _ 0-9 or $
|
106 |
|
|
# an escaped identifier is \ followed by non-whitespace
|
107 |
|
|
# why \\\\\S+ ? This gets \\\S+ in to the string then when it
|
108 |
|
|
# it used we get it searching for \ followed by non-whitespace (\S+)
|
109 |
|
|
$VID = '(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+)';
|
110 |
|
|
|
111 |
|
|
# hierarchical VID - just $VID(.$VID)+ but can't write it like this
|
112 |
|
|
# because of \ escaping (and must include whitespace after esc.ids.)
|
113 |
|
|
$HVID = '(?:(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+\s+)'.
|
114 |
|
|
'(?:\.(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+\s+))+)';
|
115 |
|
|
# V2001: added [sS] - is this correct
|
116 |
|
|
$VNUM= '(?:(?:[0-9]*\'[sS]?[bBhHdDoO]\s*[0-9A-Fa-f_zZxX?]+)|(?:[-0-9Ee._]+))';
|
117 |
|
|
|
118 |
|
|
|
119 |
|
|
$quiet=0;
|
120 |
|
|
$debug=0;
|
121 |
|
|
|
122 |
|
|
}
|
123 |
|
|
|
124 |
|
|
###########################################################################
|
125 |
|
|
|
126 |
|
|
=head1 read_verilog
|
127 |
|
|
|
128 |
|
|
reads in verilog files, parses them and stores results in an internal
|
129 |
|
|
data structure (which I call a RVP database).
|
130 |
|
|
|
131 |
|
|
Arguments: - reference to array of files to read (can have paths)
|
132 |
|
|
- reference to hash of defines with names as keys
|
133 |
|
|
- reference to array of global includes - not used anymore,
|
134 |
|
|
just kept for backwards compatibility
|
135 |
|
|
- quite flag. 1=be quiet, 0=be chatty.
|
136 |
|
|
- reference to array of include directories
|
137 |
|
|
- reference to array of library directories
|
138 |
|
|
- library extension string (eg '.v') or reference to array of strings
|
139 |
|
|
|
140 |
|
|
Returns: - a pointer to the internal data structure.
|
141 |
|
|
|
142 |
|
|
Example:
|
143 |
|
|
$defines{'TRUE'}=1; # same as +define+TRUE=1 on verilog cmd line
|
144 |
|
|
$vdata = rvp->read_verilog(\@files,[],\%defines,1,
|
145 |
|
|
\@inc_dirs,\@lib_dirs,\@lib_exts);
|
146 |
|
|
|
147 |
|
|
=cut
|
148 |
|
|
sub read_verilog {
|
149 |
|
|
# be backwards compatible with non-OO call
|
150 |
|
|
my $class = ("ARRAY" eq ref $_[0]) ? "rvp" : shift;
|
151 |
|
|
my ($files,$global_includes,$cmd_line_defines,$local_quiet,$inc_dirs,
|
152 |
|
|
$lib_dirs,$lib_ext_arg,$exp)
|
153 |
|
|
= @_;
|
154 |
|
|
my ($file,$fb,$old_quiet,@search_files,@new_search_files,$lib_exts);
|
155 |
|
|
|
156 |
|
|
my $self;
|
157 |
|
|
|
158 |
|
|
die "read_verilog needs an array ref as arg 1" unless "ARRAY" eq ref $files;
|
159 |
|
|
die "read_verilog needs an hash ref as arg 2" unless "HASH" eq ref $cmd_line_defines;
|
160 |
|
|
die "read_verilog needs 0 or 1 as arg 3" unless $local_quiet==0 || $local_quiet==1;
|
161 |
|
|
|
162 |
|
|
# be backwards compatible
|
163 |
|
|
if (!defined($inc_dirs)) { $inc_dirs=[]; }
|
164 |
|
|
if (!defined($lib_dirs)) { $lib_dirs=[]; }
|
165 |
|
|
|
166 |
|
|
if (!defined($lib_ext_arg)) { # no libexts given
|
167 |
|
|
$lib_exts=[''];
|
168 |
|
|
}
|
169 |
|
|
elsif (!ref($lib_ext_arg)) { # a string given
|
170 |
|
|
$lib_exts=[$lib_ext_arg];
|
171 |
|
|
}
|
172 |
|
|
else { # an array ref given
|
173 |
|
|
$lib_exts=$lib_ext_arg;
|
174 |
|
|
}
|
175 |
|
|
|
176 |
|
|
|
177 |
|
|
# make the parser
|
178 |
|
|
if (! defined &parse_line) {
|
179 |
|
|
|
180 |
|
|
my $perlCode=_make_parser( $debug ? [ $baseEval,$debugEval,$rvpEval ]:
|
181 |
|
|
[ $baseEval,$rvpEval ] ,
|
182 |
|
|
$debug );
|
183 |
|
|
if ($debug) {
|
184 |
|
|
open(PC,">v2html-parser.pl");
|
185 |
|
|
print PC $perlCode;
|
186 |
|
|
}
|
187 |
|
|
eval($perlCode);
|
188 |
|
|
print STDERR $@ if ($@);
|
189 |
|
|
}
|
190 |
|
|
|
191 |
|
|
if (! defined &_parse_line) { die "Parse code generation failed";}
|
192 |
|
|
|
193 |
|
|
$old_quiet=$quiet;
|
194 |
|
|
$quiet=$local_quiet;
|
195 |
|
|
# set up top of main data structure
|
196 |
|
|
$self = {};
|
197 |
|
|
$self->{files} = {}; # information on each file
|
198 |
|
|
$self->{modules} = {}; # pointers to module info in {files}
|
199 |
|
|
$self->{defines} = {};
|
200 |
|
|
$self->{ignored_modules} = {}; # list of modules were duplicates were found
|
201 |
|
|
$self->{unresolved_modules} = {}; # modules we have not found yet
|
202 |
|
|
$self->{problems} = []; # warning/confused messages
|
203 |
|
|
|
204 |
|
|
bless($self,$class);
|
205 |
|
|
|
206 |
|
|
foreach my $d (keys(%$cmd_line_defines)) {
|
207 |
|
|
_add_define($self->{defines}, $d , $cmd_line_defines->{$d}, '', 0 );
|
208 |
|
|
}
|
209 |
|
|
|
210 |
|
|
# go through all the files and find information
|
211 |
|
|
@new_search_files = @{$files};
|
212 |
|
|
while (@new_search_files) {
|
213 |
|
|
@search_files = @new_search_files;
|
214 |
|
|
@new_search_files = ();
|
215 |
|
|
foreach $file (@search_files) {
|
216 |
|
|
$self->_search($file,$inc_dirs);
|
217 |
|
|
}
|
218 |
|
|
push( @new_search_files , _resolve_modules( $self, $lib_dirs, $lib_exts ) );
|
219 |
|
|
}
|
220 |
|
|
|
221 |
|
|
|
222 |
|
|
if ($debug) {
|
223 |
|
|
_check_coverage();
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
# cross reference files' information
|
227 |
|
|
print "Cross referencing\n" unless $quiet;
|
228 |
|
|
$self->_cross_reference();
|
229 |
|
|
|
230 |
|
|
$quiet=$old_quiet;
|
231 |
|
|
|
232 |
|
|
foreach my $m ( sort (keys %{$self->{unresolved_modules}} )) {
|
233 |
|
|
# find somewhere it is instantiated for warning message
|
234 |
|
|
my $file="";
|
235 |
|
|
my $line="";
|
236 |
|
|
foreach my $m2 (sort (keys %{$self->{modules}})) {
|
237 |
|
|
foreach my $inst (@{$self->{modules}{$m2}{instances}}) {
|
238 |
|
|
if ($inst->{module} eq $m) {
|
239 |
|
|
$file = $inst->{file};
|
240 |
|
|
$line = $inst->{line};
|
241 |
|
|
last;
|
242 |
|
|
}
|
243 |
|
|
}
|
244 |
|
|
}
|
245 |
25 |
alirezamon |
#$self->_add_warning("$file:$line: Could not find module $m");
|
246 |
16 |
alirezamon |
}
|
247 |
|
|
return $self;
|
248 |
|
|
}
|
249 |
|
|
|
250 |
|
|
###########################################################################
|
251 |
|
|
|
252 |
|
|
=head1 get_problems
|
253 |
|
|
|
254 |
|
|
Return any problems that happened during parsing
|
255 |
|
|
|
256 |
|
|
Returns: - array of strings of problems. Each one is:
|
257 |
|
|
"TYPE:FILE:LINE: description"
|
258 |
|
|
|
259 |
|
|
=cut
|
260 |
|
|
sub get_problems {
|
261 |
|
|
my ($self) = @_;
|
262 |
|
|
|
263 |
|
|
return (@{$self->{problems}});
|
264 |
|
|
}
|
265 |
|
|
|
266 |
|
|
###########################################################################
|
267 |
|
|
|
268 |
|
|
=head1 set_debug
|
269 |
|
|
|
270 |
|
|
Turns on debug printing in the parser.
|
271 |
|
|
|
272 |
|
|
Returns: - nothing
|
273 |
|
|
|
274 |
|
|
=cut
|
275 |
|
|
sub set_debug {
|
276 |
|
|
$debug=1;
|
277 |
|
|
}
|
278 |
|
|
|
279 |
|
|
###########################################################################
|
280 |
|
|
|
281 |
|
|
=head1 unset_debug
|
282 |
|
|
|
283 |
|
|
Turns off debug printing in the parser.
|
284 |
|
|
|
285 |
|
|
Returns: - nothing
|
286 |
|
|
|
287 |
|
|
=cut
|
288 |
|
|
sub unset_debug {
|
289 |
|
|
$debug=0;
|
290 |
|
|
}
|
291 |
|
|
|
292 |
|
|
###########################################################################
|
293 |
|
|
|
294 |
|
|
=head1 get_files
|
295 |
|
|
|
296 |
|
|
Get a list of all the files in the database.
|
297 |
|
|
|
298 |
|
|
Returns: - list of all the files
|
299 |
|
|
|
300 |
|
|
Example: @all_files = $vdata->get_files();
|
301 |
|
|
|
302 |
|
|
=cut
|
303 |
|
|
sub get_files{
|
304 |
|
|
my ($self) = @_;
|
305 |
|
|
|
306 |
|
|
if (wantarray) {
|
307 |
|
|
return sort (keys %{$self->{files}});
|
308 |
|
|
}
|
309 |
|
|
else { # in a scalar context keys returns the number of elements - sort doesn't
|
310 |
|
|
return keys %{$self->{files}};
|
311 |
|
|
}
|
312 |
|
|
}
|
313 |
|
|
|
314 |
|
|
###########################################################################
|
315 |
|
|
|
316 |
|
|
=head1 get_files_modules
|
317 |
|
|
|
318 |
|
|
Get a list of all the modules in a particular file.
|
319 |
|
|
|
320 |
|
|
Arguments: - name of file
|
321 |
|
|
|
322 |
|
|
Returns: - list of module names
|
323 |
|
|
|
324 |
|
|
Example: @modules = $vdata->get_files_modules($file);
|
325 |
|
|
|
326 |
|
|
=cut
|
327 |
|
|
sub get_files_modules{
|
328 |
|
|
my ($self,$file) = @_;
|
329 |
|
|
my (@modules,$m);
|
330 |
|
|
|
331 |
|
|
foreach $m (sort (keys %{$self->{files}{$file}{modules}})) {
|
332 |
|
|
push(@modules,$m)
|
333 |
|
|
}
|
334 |
|
|
|
335 |
|
|
return @modules;
|
336 |
|
|
}
|
337 |
|
|
|
338 |
|
|
|
339 |
|
|
###########################################################################
|
340 |
|
|
|
341 |
|
|
=head1 get_files_full_name
|
342 |
|
|
|
343 |
|
|
Get the full name (including path) of a file.
|
344 |
|
|
|
345 |
|
|
Arguments: - name of file
|
346 |
|
|
|
347 |
|
|
Returns: - full path name
|
348 |
|
|
|
349 |
|
|
Example $full_name = $vdata->get_files_full_name($file);
|
350 |
|
|
|
351 |
|
|
=cut
|
352 |
|
|
sub get_files_full_name{
|
353 |
|
|
my ($self,$file) = @_;
|
354 |
|
|
|
355 |
|
|
return $self->{files}{$file}{full_name};
|
356 |
|
|
|
357 |
|
|
}
|
358 |
|
|
|
359 |
|
|
###########################################################################
|
360 |
|
|
|
361 |
|
|
=head1 get_files_stats
|
362 |
|
|
|
363 |
|
|
Get statistics about a file
|
364 |
|
|
|
365 |
|
|
Arguments: - name of file
|
366 |
|
|
|
367 |
|
|
Returns: - number of lines in the file (more later...)
|
368 |
|
|
|
369 |
|
|
Example $full_name = $vdata->get_files_stats($file);
|
370 |
|
|
|
371 |
|
|
=cut
|
372 |
|
|
sub get_files_stats{
|
373 |
|
|
my ($self,$file) = @_;
|
374 |
|
|
|
375 |
|
|
return $self->{files}{$file}{lines};
|
376 |
|
|
|
377 |
|
|
}
|
378 |
|
|
|
379 |
|
|
###########################################################################
|
380 |
|
|
|
381 |
|
|
=head1 file_exists
|
382 |
|
|
|
383 |
|
|
Test if a particular module file in the database.
|
384 |
|
|
|
385 |
|
|
Arguments: - file name to test.
|
386 |
|
|
|
387 |
|
|
Returns: - 1 if exists otherwise 0
|
388 |
|
|
|
389 |
|
|
Example: if ($vdata->file_exists($file))....
|
390 |
|
|
|
391 |
|
|
=cut
|
392 |
|
|
sub file_exists{
|
393 |
|
|
my ($self,$file) = @_;
|
394 |
|
|
return exists($self->{files}{_ffile($file)});
|
395 |
|
|
}
|
396 |
|
|
|
397 |
|
|
###########################################################################
|
398 |
|
|
|
399 |
|
|
=head1 get_modules
|
400 |
|
|
|
401 |
|
|
Get a list of all the modules in the database.
|
402 |
|
|
|
403 |
|
|
|
404 |
|
|
Returns: - list of all the modules
|
405 |
|
|
|
406 |
|
|
Example: @all_modules = $vdata->get_modules();
|
407 |
|
|
|
408 |
|
|
=cut
|
409 |
|
|
sub get_modules{
|
410 |
|
|
my ($self) = @_;
|
411 |
|
|
|
412 |
|
|
if (wantarray) {
|
413 |
|
|
return sort (keys %{$self->{modules}});
|
414 |
|
|
}
|
415 |
|
|
else { # in a scalar context keys returns the number of elements - sort doesn't
|
416 |
|
|
return keys %{$self->{modules}};
|
417 |
|
|
}
|
418 |
|
|
|
419 |
|
|
}
|
420 |
|
|
|
421 |
|
|
|
422 |
|
|
###########################################################################
|
423 |
|
|
|
424 |
|
|
=head1 get_modules_t_and_f
|
425 |
|
|
|
426 |
|
|
Get a list of all the tasks and functions in a particular module.
|
427 |
|
|
|
428 |
|
|
Arguments: - name of module
|
429 |
|
|
|
430 |
|
|
Returns: - list of tasks and function names
|
431 |
|
|
|
432 |
|
|
Example: if ( @t_and_f = $vdata->get_modules_t_and_f($m))...
|
433 |
|
|
|
434 |
|
|
=cut
|
435 |
|
|
# return a list of all the tasks and functions in a module
|
436 |
|
|
sub get_modules_t_and_f{
|
437 |
|
|
my ($self,$module) = @_;
|
438 |
|
|
|
439 |
|
|
if (wantarray) {
|
440 |
|
|
return sort (keys %{$self->{modules}{$module}{t_and_f}});
|
441 |
|
|
}
|
442 |
|
|
else { # in a scalar context keys returns the number of elements - sort doesn't
|
443 |
|
|
return keys %{$self->{modules}{$module}{t_and_f}};
|
444 |
|
|
}
|
445 |
|
|
|
446 |
|
|
}
|
447 |
|
|
|
448 |
|
|
###########################################################################
|
449 |
|
|
|
450 |
|
|
=head1 get_modules_t_or_f
|
451 |
|
|
|
452 |
|
|
Get information on a task or function in a module.
|
453 |
|
|
|
454 |
|
|
Arguments: - module name
|
455 |
|
|
- task or function name
|
456 |
|
|
|
457 |
|
|
Returns: - A 4 element list: type (task or function), definition line,
|
458 |
|
|
file, anchor
|
459 |
|
|
|
460 |
|
|
Example: ($t_type,$t_line ,$t_file,$t_anchor)=
|
461 |
|
|
$vdata->get_modules_t_or_f($m,$tf);
|
462 |
|
|
|
463 |
|
|
=cut
|
464 |
|
|
sub get_modules_t_or_f{
|
465 |
|
|
my ($self,$mod,$t_or_f) = @_;
|
466 |
|
|
|
467 |
|
|
if (exists($self->{modules}{$mod}{t_and_f}{$t_or_f})) {
|
468 |
|
|
return($self->{modules}{$mod}{t_and_f}{$t_or_f}{type},
|
469 |
|
|
$self->{modules}{$mod}{t_and_f}{$t_or_f}{line},
|
470 |
|
|
$self->{modules}{$mod}{t_and_f}{$t_or_f}{file},
|
471 |
|
|
$self->{modules}{$mod}{t_and_f}{$t_or_f}{anchor});
|
472 |
|
|
}
|
473 |
|
|
else {
|
474 |
|
|
return ();
|
475 |
|
|
}
|
476 |
|
|
}
|
477 |
|
|
|
478 |
|
|
###########################################################################
|
479 |
|
|
|
480 |
|
|
=head1 get_modules_signals
|
481 |
|
|
|
482 |
|
|
Get a list of all the signals in a particular module.
|
483 |
|
|
|
484 |
|
|
Arguments: - name of module
|
485 |
|
|
|
486 |
|
|
Returns: - list of signal names
|
487 |
|
|
|
488 |
|
|
Example: if ( @signs = $vdata->get_modules_signals($m))...
|
489 |
|
|
|
490 |
|
|
=cut
|
491 |
|
|
# return a list of all the tasks and functions in a module
|
492 |
|
|
sub get_modules_signals{
|
493 |
|
|
my ($self,$module) = @_;
|
494 |
|
|
|
495 |
|
|
if (wantarray) {
|
496 |
|
|
return sort (keys %{$self->{modules}{$module}{signals}});
|
497 |
|
|
}
|
498 |
|
|
else { # in a scalar context keys returns the number of elements - sort doesn't
|
499 |
|
|
return keys %{$self->{modules}{$module}{signals}};
|
500 |
|
|
}
|
501 |
|
|
|
502 |
|
|
}
|
503 |
|
|
|
504 |
|
|
###########################################################################
|
505 |
|
|
|
506 |
|
|
=head1 get_modules_file
|
507 |
|
|
|
508 |
|
|
Get the file name (no path) that a module is defined in.
|
509 |
|
|
|
510 |
|
|
Arguments: - module name
|
511 |
|
|
|
512 |
|
|
Returns: - file name without path, and the line number module starts on
|
513 |
|
|
|
514 |
|
|
Example: ($f) = $vdata->get_modules_file($m);
|
515 |
|
|
|
516 |
|
|
=cut
|
517 |
|
|
# get the file name that contains a module
|
518 |
|
|
sub get_modules_file{
|
519 |
|
|
my ($self,$module) = @_;
|
520 |
|
|
|
521 |
|
|
return ($self->{modules}{$module}{file},$self->{modules}{$module}{line});
|
522 |
|
|
}
|
523 |
|
|
|
524 |
|
|
|
525 |
|
|
###########################################################################
|
526 |
|
|
|
527 |
|
|
=head1 get_modules_type
|
528 |
|
|
|
529 |
|
|
Get the type of the module - It is one of: module, macromodule or primitive
|
530 |
|
|
(rvp treats these all as modules).
|
531 |
|
|
|
532 |
|
|
Arguments: - module name
|
533 |
|
|
|
534 |
|
|
Returns: - type
|
535 |
|
|
|
536 |
|
|
Example: $t = $vdata->get_modules_type($m);
|
537 |
|
|
|
538 |
|
|
=cut
|
539 |
|
|
# get the file name that contains a module
|
540 |
|
|
sub get_modules_type{
|
541 |
|
|
my ($self,$module) = @_;
|
542 |
|
|
|
543 |
|
|
return ($self->{modules}{$module}{type});
|
544 |
|
|
}
|
545 |
|
|
|
546 |
|
|
###########################################################################
|
547 |
|
|
|
548 |
|
|
=head1 get_files_includes
|
549 |
|
|
|
550 |
|
|
Get the file names (no path) of files included in a file.
|
551 |
|
|
|
552 |
|
|
Arguments: - file name
|
553 |
|
|
|
554 |
|
|
Returns: - list of file names without paths
|
555 |
|
|
|
556 |
|
|
Example: @f = $vdata->get_files_includes($file);
|
557 |
|
|
|
558 |
|
|
=cut
|
559 |
|
|
sub get_files_includes {
|
560 |
|
|
my ($self,$f) = @_;
|
561 |
|
|
my @includes_found = ();
|
562 |
|
|
|
563 |
|
|
if (exists($self->{files}{$f})) {
|
564 |
|
|
foreach my $inc ( sort ( keys %{$self->{files}{$f}{includes}} )) {
|
565 |
|
|
push(@includes_found,$inc);
|
566 |
|
|
# do the includes for the included file
|
567 |
|
|
push(@includes_found, $self->get_files_includes($inc));
|
568 |
|
|
}
|
569 |
|
|
}
|
570 |
|
|
|
571 |
|
|
return @includes_found;
|
572 |
|
|
}
|
573 |
|
|
|
574 |
|
|
###########################################################################
|
575 |
|
|
|
576 |
|
|
=head1 get_files_included_by
|
577 |
|
|
|
578 |
|
|
Get the file names (no path) of files that included this file.
|
579 |
|
|
|
580 |
|
|
Arguments: - file name
|
581 |
|
|
|
582 |
|
|
Returns: - list of file names without paths
|
583 |
|
|
|
584 |
|
|
Example: @f = $vdata->get_files_included_by($file);
|
585 |
|
|
|
586 |
|
|
=cut
|
587 |
|
|
sub get_files_included_by {
|
588 |
|
|
my ($self,$f) = @_;
|
589 |
|
|
|
590 |
|
|
return @{$self->{files}{$f}{included_by}};
|
591 |
|
|
|
592 |
|
|
}
|
593 |
|
|
|
594 |
|
|
|
595 |
|
|
###########################################################################
|
596 |
|
|
|
597 |
|
|
=head1 module_ignored
|
598 |
|
|
|
599 |
|
|
Test if a particular module has been ignored because of duplicates found
|
600 |
|
|
|
601 |
|
|
Arguments: - module name to test
|
602 |
|
|
|
603 |
|
|
Returns: - 1 if ignored otherwise 0
|
604 |
|
|
|
605 |
|
|
Example: if ($vdata->module_ignored($module))....
|
606 |
|
|
|
607 |
|
|
=cut
|
608 |
|
|
sub module_ignored {
|
609 |
|
|
my ($self,$module) = @_;
|
610 |
|
|
return (exists($self->{modules}{$module}) &&
|
611 |
|
|
$self->{modules}{$module}{duplicate});
|
612 |
|
|
}
|
613 |
|
|
|
614 |
|
|
###########################################################################
|
615 |
|
|
|
616 |
|
|
=head1 module_exists
|
617 |
|
|
|
618 |
|
|
Test if a particular module exists in the database.
|
619 |
|
|
|
620 |
|
|
Arguments: - module name to test
|
621 |
|
|
|
622 |
|
|
Returns: - 1 if exists otherwise 0
|
623 |
|
|
|
624 |
|
|
Example: if ($vdata->module_exists($module))....
|
625 |
|
|
|
626 |
|
|
=cut
|
627 |
|
|
sub module_exists{
|
628 |
|
|
my ($self,$module) = @_;
|
629 |
|
|
return exists($self->{modules}{$module});
|
630 |
|
|
}
|
631 |
|
|
|
632 |
|
|
###########################################################################
|
633 |
|
|
|
634 |
|
|
=head1 get_ignored_modules
|
635 |
|
|
|
636 |
|
|
Return a list of the ignored modules. These are modules where duplicates
|
637 |
|
|
have been found.
|
638 |
|
|
|
639 |
|
|
Returns: - List of ignored modules
|
640 |
|
|
|
641 |
|
|
Example: - foreach $module ($vdata->get_ignored_modules())....
|
642 |
|
|
|
643 |
|
|
=cut
|
644 |
|
|
sub get_ignored_modules {
|
645 |
|
|
my ($self) = @_;
|
646 |
|
|
my @ig =();
|
647 |
|
|
foreach my $m (sort (keys %{$self->{modules}})) {
|
648 |
|
|
push(@ig, $m) if ($self->{modules}{$m}{duplicate});
|
649 |
|
|
}
|
650 |
|
|
return @ig;
|
651 |
|
|
}
|
652 |
|
|
|
653 |
|
|
###########################################################################
|
654 |
|
|
|
655 |
|
|
=head1 get_module_signal
|
656 |
|
|
|
657 |
|
|
Get information about a particular signal in a particular module.
|
658 |
|
|
|
659 |
|
|
Arguments: - name of module
|
660 |
|
|
- name of signal
|
661 |
|
|
|
662 |
|
|
Returns: - A list containing:
|
663 |
|
|
- the line signal is defined
|
664 |
|
|
- the line signal is assigned first (or -1)
|
665 |
|
|
- line in instantiating module where an input
|
666 |
|
|
is driven from (or -1)
|
667 |
|
|
- the type of the signal (input,output,reg etc)
|
668 |
|
|
- the file the signal is in
|
669 |
|
|
- posedge flag (1 if signal ever seen with posedge)
|
670 |
|
|
- negedge flag (1 if signal ever seen with negedge)
|
671 |
|
|
- second type (eg reg for a registered output)
|
672 |
|
|
- signal real source file
|
673 |
|
|
- signal real source line
|
674 |
|
|
- range string if any ( not including [ and ] )
|
675 |
|
|
- the file signal is assigned first (or '')
|
676 |
|
|
- file for the instantiating module where an input
|
677 |
|
|
is driven from (or "")
|
678 |
|
|
- a pointer to an array of dimensions for memories
|
679 |
|
|
each element of the array is a dimension, array
|
680 |
|
|
is empty for non-memories
|
681 |
|
|
|
682 |
|
|
Note posedge and negedge information is propagated up the hierarchy to
|
683 |
|
|
attached signals. It is not propagated down the hierarchy.
|
684 |
|
|
|
685 |
|
|
Example: ($s_line,$s_a_line,$s_i_line,$s_type,$s_file,$s_p,$s_n,
|
686 |
|
|
$s_type2,$s_r_file,$s_r_line,$range,$s_a_file,$s_i_file) =
|
687 |
|
|
$vdata->get_module_signal($m,$sig);
|
688 |
|
|
|
689 |
|
|
=cut
|
690 |
|
|
sub get_module_signal{
|
691 |
|
|
my ($self,$module,$sig) = @_;
|
692 |
|
|
|
693 |
|
|
if (exists( $self->{modules}{$module}{signals}{$sig} )) {
|
694 |
|
|
return ($self->{modules}{$module}{signals}{$sig}{line},
|
695 |
|
|
$self->{modules}{$module}{signals}{$sig}{a_line},
|
696 |
|
|
$self->{modules}{$module}{signals}{$sig}{i_line},
|
697 |
|
|
$self->{modules}{$module}{signals}{$sig}{type},
|
698 |
|
|
$self->{modules}{$module}{signals}{$sig}{file},
|
699 |
|
|
$self->{modules}{$module}{signals}{$sig}{posedge},
|
700 |
|
|
$self->{modules}{$module}{signals}{$sig}{negedge},
|
701 |
|
|
$self->{modules}{$module}{signals}{$sig}{type2},
|
702 |
|
|
$self->{modules}{$module}{signals}{$sig}{source}{file},
|
703 |
|
|
$self->{modules}{$module}{signals}{$sig}{source}{line},
|
704 |
|
|
$self->{modules}{$module}{signals}{$sig}{range},
|
705 |
|
|
$self->{modules}{$module}{signals}{$sig}{a_file},
|
706 |
|
|
$self->{modules}{$module}{signals}{$sig}{i_file},
|
707 |
|
|
$self->{modules}{$module}{signals}{$sig}{dimensions});
|
708 |
|
|
}
|
709 |
|
|
else {
|
710 |
|
|
return ();
|
711 |
|
|
}
|
712 |
|
|
}
|
713 |
|
|
|
714 |
|
|
###########################################################################
|
715 |
|
|
|
716 |
|
|
=head1 get_first_signal_port_con
|
717 |
|
|
|
718 |
|
|
Get the first port that this signal in this module is connected to.
|
719 |
|
|
|
720 |
|
|
Arguments: - module name
|
721 |
|
|
- signal name
|
722 |
|
|
|
723 |
|
|
Returns: - a 5 element list: instantiated module name, instance name
|
724 |
|
|
port name, line number and file
|
725 |
|
|
|
726 |
|
|
Example: ($im,$in,$p,$l,$f)=$vdata->get_first_signal_port_con($m,$s);
|
727 |
|
|
|
728 |
|
|
=cut
|
729 |
|
|
sub get_first_signal_port_con{
|
730 |
|
|
my ($self,$module,$signal) = @_;
|
731 |
|
|
|
732 |
|
|
$self->{current_signal_port_con} =0;
|
733 |
|
|
$self->{current_signal_port_con_module}=$module;
|
734 |
|
|
$self->{current_signal_port_con_module_signal}=$signal;
|
735 |
|
|
|
736 |
|
|
return $self->get_next_signal_port_con();
|
737 |
|
|
}
|
738 |
|
|
|
739 |
|
|
###########################################################################
|
740 |
|
|
|
741 |
|
|
=head1 get_next_signal_port_con
|
742 |
|
|
|
743 |
|
|
Get the next port that this signal in this module is connected to.
|
744 |
|
|
|
745 |
|
|
Returns: - a 5 element list: instantiated module name, instance name
|
746 |
|
|
port name, line number and file
|
747 |
|
|
|
748 |
|
|
Example: ($im,$in,$p,$l,$f)=$vdata->get_next_signal_port_con();
|
749 |
|
|
|
750 |
|
|
=cut
|
751 |
|
|
sub get_next_signal_port_con{
|
752 |
|
|
my ($self) = @_;
|
753 |
|
|
my ($module,$signal,$i,$pcref);
|
754 |
|
|
|
755 |
|
|
$module = $self->{current_signal_port_con_module};
|
756 |
|
|
$signal = $self->{current_signal_port_con_module_signal};
|
757 |
|
|
$i = $self->{current_signal_port_con};
|
758 |
|
|
|
759 |
|
|
$pcref = $self->{modules}{$module}{signals}{$signal}{port_con};
|
760 |
|
|
if (@{$pcref} > $i ) {
|
761 |
|
|
$self->{current_signal_port_con}++;
|
762 |
|
|
return ( $pcref->[$i]{module},$pcref->[$i]{inst},$pcref->[$i]{port},
|
763 |
|
|
$pcref->[$i]{line},$pcref->[$i]{file});
|
764 |
|
|
}
|
765 |
|
|
else {
|
766 |
|
|
return ();
|
767 |
|
|
}
|
768 |
|
|
}
|
769 |
|
|
|
770 |
|
|
###########################################################################
|
771 |
|
|
|
772 |
|
|
=head1 get_first_signal_con_to
|
773 |
|
|
|
774 |
|
|
Get the first signal that is connected to this port in an
|
775 |
|
|
instantiation of this module. This only works for instances that use
|
776 |
|
|
the .port(sig) notation.
|
777 |
|
|
|
778 |
|
|
Arguments: - module name
|
779 |
|
|
- signal name
|
780 |
|
|
|
781 |
|
|
Returns: - a 4 element list: signal connected to this port
|
782 |
|
|
module signal is in
|
783 |
|
|
instance (of this module) where the connection
|
784 |
|
|
occurs
|
785 |
|
|
|
786 |
|
|
Example: ($cts,$ctm,$cti)=$vdata->get_first_signal_con_to($m,$s);
|
787 |
|
|
|
788 |
|
|
=cut
|
789 |
|
|
sub get_first_signal_con_to{
|
790 |
|
|
my ($self,$module,$signal) = @_;
|
791 |
|
|
|
792 |
|
|
$self->{current_signal_con_to} =0;
|
793 |
|
|
$self->{current_signal_con_to_module}=$module;
|
794 |
|
|
$self->{current_signal_con_to_module_signal}=$signal;
|
795 |
|
|
|
796 |
|
|
return $self->get_next_signal_con_to();
|
797 |
|
|
}
|
798 |
|
|
|
799 |
|
|
###########################################################################
|
800 |
|
|
|
801 |
|
|
=head1 get_next_signal_con_to
|
802 |
|
|
|
803 |
|
|
Get the next signal that is connected to this port in an
|
804 |
|
|
instantiation of this module. This only works for instances that use
|
805 |
|
|
the .port(sig) notation.
|
806 |
|
|
|
807 |
|
|
Arguments: - module name
|
808 |
|
|
- signal name
|
809 |
|
|
|
810 |
|
|
Returns: - a 4 element list: signal connected to this port
|
811 |
|
|
module signal is in
|
812 |
|
|
instance (of this module) where the connection
|
813 |
|
|
occurs
|
814 |
|
|
|
815 |
|
|
Example: ($cts,$ctm,$cti)=$vdata->get_next_signal_con_to();
|
816 |
|
|
|
817 |
|
|
=cut
|
818 |
|
|
sub get_next_signal_con_to{
|
819 |
|
|
my ($self) = @_;
|
820 |
|
|
my ($module,$signal,$i,$ctref);
|
821 |
|
|
|
822 |
|
|
$module = $self->{current_signal_con_to_module};
|
823 |
|
|
$signal = $self->{current_signal_con_to_module_signal};
|
824 |
|
|
$i = $self->{current_signal_con_to};
|
825 |
|
|
|
826 |
|
|
$ctref = $self->{modules}{$module}{signals}{$signal}{con_to};
|
827 |
|
|
if (@{$ctref} > $i ) {
|
828 |
|
|
$self->{current_signal_con_to}++;
|
829 |
|
|
return ( $ctref->[$i]{signal},$ctref->[$i]{module},$ctref->[$i]{inst});
|
830 |
|
|
}
|
831 |
|
|
else {
|
832 |
|
|
return ();
|
833 |
|
|
}
|
834 |
|
|
}
|
835 |
|
|
|
836 |
|
|
###########################################################################
|
837 |
|
|
|
838 |
|
|
=head1 get_first_instantiator
|
839 |
|
|
|
840 |
|
|
Get the first thing that instantiates this module.
|
841 |
|
|
|
842 |
|
|
Arguments: - module name
|
843 |
|
|
|
844 |
|
|
Returns: - a 4 element list: instantiating module, file, instance name, line
|
845 |
|
|
|
846 |
|
|
Example:
|
847 |
|
|
($im,$f,$i) = $vdata->get_first_instantiator($m );
|
848 |
|
|
|
849 |
|
|
=cut
|
850 |
|
|
# Get the first thing that instantiates or empty list if none.
|
851 |
|
|
# Returns: { module, file, inst }
|
852 |
|
|
sub get_first_instantiator{
|
853 |
|
|
my ($self,$module) = @_;
|
854 |
|
|
|
855 |
|
|
if ( exists( $self->{modules}{$module} )) {
|
856 |
|
|
$self->{current_instantiator} =0;
|
857 |
|
|
$self->{current_instantiator_module}=$module;
|
858 |
|
|
return $self->get_next_instantiator();
|
859 |
|
|
}
|
860 |
|
|
else {
|
861 |
|
|
return ();
|
862 |
|
|
}
|
863 |
|
|
}
|
864 |
|
|
|
865 |
|
|
###########################################################################
|
866 |
|
|
|
867 |
|
|
=head1 get_next_instantiator
|
868 |
|
|
|
869 |
|
|
Get the first thing that instantiates the module specified in
|
870 |
|
|
get_first_instantiator (or _by_context).
|
871 |
|
|
|
872 |
|
|
Returns: - a 4 element list: instantiating module, file,
|
873 |
|
|
instance name, line
|
874 |
|
|
|
875 |
|
|
Example:
|
876 |
|
|
($im,$f,$i) = $vdata->get_next_instantiator();
|
877 |
|
|
|
878 |
|
|
=cut
|
879 |
|
|
sub get_next_instantiator{
|
880 |
|
|
my ($self) = @_;
|
881 |
|
|
my ($module,$i);
|
882 |
|
|
|
883 |
|
|
$module = $self->{current_instantiator_module};
|
884 |
|
|
$i = $self->{current_instantiator};
|
885 |
|
|
|
886 |
|
|
if (@{$self->{modules}{$module}{inst_by}} > $i ) {
|
887 |
|
|
$self->{current_instantiator}++;
|
888 |
|
|
return ($self->{modules}{$module}{inst_by}[$i]{module},
|
889 |
|
|
$self->{modules}{$module}{inst_by}[$i]{file},
|
890 |
|
|
$self->{modules}{$module}{inst_by}[$i]{inst},
|
891 |
|
|
$self->{modules}{$module}{inst_by}[$i]{line} );
|
892 |
|
|
}
|
893 |
|
|
else {
|
894 |
|
|
return ();
|
895 |
|
|
}
|
896 |
|
|
}
|
897 |
|
|
|
898 |
|
|
###########################################################################
|
899 |
|
|
|
900 |
|
|
=head1 get_first_instantiation
|
901 |
|
|
|
902 |
|
|
Get the first thing that this module instantiates.
|
903 |
|
|
|
904 |
|
|
Arguments: - module name
|
905 |
|
|
|
906 |
|
|
Returns: - a 4 element list: instantiated module name, file,
|
907 |
|
|
instance name, and line number
|
908 |
|
|
|
909 |
|
|
Example:
|
910 |
|
|
($im,$f,$i,$l) = $vdata->get_first_instantiation($m);
|
911 |
|
|
|
912 |
|
|
=cut
|
913 |
|
|
sub get_first_instantiation{
|
914 |
|
|
my ($self,$module) = @_;
|
915 |
|
|
|
916 |
|
|
if ( exists( $self->{modules}{$module} )) {
|
917 |
|
|
$self->{current_instantiation} =0;
|
918 |
|
|
$self->{current_instantiation_module}=$module;
|
919 |
|
|
return $self->get_next_instantiation();
|
920 |
|
|
}
|
921 |
|
|
else {
|
922 |
|
|
return ();
|
923 |
|
|
}
|
924 |
|
|
}
|
925 |
|
|
|
926 |
|
|
###########################################################################
|
927 |
|
|
|
928 |
|
|
=head1 get_next_instantiation
|
929 |
|
|
|
930 |
|
|
Get the next thing that this module instantiates.
|
931 |
|
|
|
932 |
|
|
|
933 |
|
|
Returns: - a 4 element list: instantiated module name, file,
|
934 |
|
|
instance name, and line number
|
935 |
|
|
|
936 |
|
|
Example:
|
937 |
|
|
($im,$f,$i,$l) = $vdata->get_next_instantiation();
|
938 |
|
|
|
939 |
|
|
=cut
|
940 |
|
|
sub get_next_instantiation{
|
941 |
|
|
my ($self) = @_;
|
942 |
|
|
my ($module,$i);
|
943 |
|
|
|
944 |
|
|
$module = $self->{current_instantiation_module};
|
945 |
|
|
$i = $self->{current_instantiation};
|
946 |
|
|
|
947 |
|
|
if (@{$self->{modules}{$module}{instances}} > $i ) {
|
948 |
|
|
$self->{current_instantiation}++;
|
949 |
|
|
return ($self->{modules}{$module}{instances}[$i]{module},
|
950 |
|
|
$self->{modules}{$module}{instances}[$i]{file},
|
951 |
|
|
$self->{modules}{$module}{instances}[$i]{inst_name},
|
952 |
|
|
$self->{modules}{$module}{instances}[$i]{line} );
|
953 |
|
|
}
|
954 |
|
|
else {
|
955 |
|
|
return ();
|
956 |
|
|
}
|
957 |
|
|
}
|
958 |
|
|
|
959 |
|
|
###########################################################################
|
960 |
|
|
|
961 |
|
|
=head1 get_current_instantiations_port_con
|
962 |
|
|
|
963 |
|
|
Gets the port connections for the current instantiations (which is got
|
964 |
|
|
using get_first_instantiation and get_next_instantiation). If the
|
965 |
|
|
instantiation does not use .port(...) syntax and rvp does not have the
|
966 |
|
|
access to the source of the module then the port names will be returned as
|
967 |
|
|
numbers in connection order starting at 0.
|
968 |
|
|
|
969 |
|
|
|
970 |
|
|
Returns: - A hash (well, really a list that can be assigned to a hash).
|
971 |
|
|
The keys of the hash are the port names. The values of the
|
972 |
|
|
hash is everything (except comments) that appeared in the
|
973 |
|
|
brackets in the verilog.
|
974 |
|
|
|
975 |
|
|
Example: %port_con = $vdata->get_current_instantiations_port_con();
|
976 |
|
|
foreach $port (keys %port_con) { ...
|
977 |
|
|
|
978 |
|
|
=cut
|
979 |
|
|
sub get_current_instantiations_port_con{
|
980 |
|
|
my ($self) = @_;
|
981 |
|
|
my ($module,$i);
|
982 |
|
|
|
983 |
|
|
$module = $self->{current_instantiation_module};
|
984 |
|
|
$i = $self->{current_instantiation} - 1;
|
985 |
|
|
|
986 |
|
|
if (@{$self->{modules}{$module}{instances}} > $i ) {
|
987 |
|
|
return (%{$self->{modules}{$module}{instances}[$i]{connections}});
|
988 |
|
|
}
|
989 |
|
|
else {
|
990 |
|
|
return {};
|
991 |
|
|
}
|
992 |
|
|
}
|
993 |
|
|
|
994 |
|
|
###########################################################################
|
995 |
|
|
|
996 |
|
|
=head1 get_current_instantiations_parameters
|
997 |
|
|
|
998 |
|
|
Gets the parameters for the current instantiations (which is set using
|
999 |
|
|
get_first_instantiation and get_next_instantiation). If the
|
1000 |
|
|
instantiation parameters does not use the verilog 2001 .name(...)
|
1001 |
|
|
syntax and rvp does not have the access to the source of the module
|
1002 |
|
|
then the parameter names will be returned as numbers reflecting the
|
1003 |
|
|
order (starting at 0).
|
1004 |
|
|
|
1005 |
|
|
|
1006 |
|
|
Returns: - A hash (well, really a list that can be assigned to a hash).
|
1007 |
|
|
The keys of the hash are the parameters names. The values of the
|
1008 |
|
|
hash is everything (except comments) in the value.
|
1009 |
|
|
|
1010 |
|
|
Example: %parameters = $vdata->get_current_instantiations_parameters();
|
1011 |
|
|
foreach my $p (keys %parameters) { ...
|
1012 |
|
|
|
1013 |
|
|
=cut
|
1014 |
|
|
sub get_current_instantiations_parameters{
|
1015 |
|
|
my ($self) = @_;
|
1016 |
|
|
my ($module,$i);
|
1017 |
|
|
|
1018 |
|
|
$module = $self->{current_instantiation_module};
|
1019 |
|
|
$i = $self->{current_instantiation} - 1;
|
1020 |
|
|
|
1021 |
|
|
my %r;
|
1022 |
|
|
if (@{$self->{modules}{$module}{instances}} > $i ) {
|
1023 |
|
|
foreach my $p (keys %{$self->{modules}{$module}{instances}[$i]{parameters}}) {
|
1024 |
|
|
$r{$p}=$self->{modules}{$module}{instances}[$i]{parameters}{$p}{value};
|
1025 |
|
|
}
|
1026 |
|
|
}
|
1027 |
|
|
|
1028 |
|
|
return %r;
|
1029 |
|
|
}
|
1030 |
|
|
|
1031 |
|
|
###########################################################################
|
1032 |
|
|
|
1033 |
|
|
=head1 get_modules_parameters
|
1034 |
|
|
|
1035 |
|
|
Gets the parameters for a module.
|
1036 |
|
|
|
1037 |
|
|
Arguments: - module name
|
1038 |
|
|
|
1039 |
|
|
Returns: - A hash (well, really a list that can be assigned to a hash).
|
1040 |
|
|
The keys of the hash are the parameters names. The values of the
|
1041 |
|
|
hash is everything (except comments) in the value.
|
1042 |
|
|
|
1043 |
|
|
Example: %parameters = $vdata->get_modules_parameters();
|
1044 |
|
|
foreach my $p (keys %parameters) { ...
|
1045 |
|
|
|
1046 |
|
|
=cut
|
1047 |
|
|
sub get_modules_parameters{
|
1048 |
|
|
my ($self,$module) = @_;
|
1049 |
|
|
|
1050 |
|
|
my %r;
|
1051 |
|
|
foreach my $p (keys %{$self->{modules}{$module}{parameters}}) {
|
1052 |
|
|
$r{$p}=$self->{modules}{$module}{parameters}{$p}{value};
|
1053 |
|
|
}
|
1054 |
|
|
return %r;
|
1055 |
|
|
}
|
1056 |
|
|
|
1057 |
|
|
#######################################################
|
1058 |
|
|
################### Modified ###########################
|
1059 |
|
|
#######################################################
|
1060 |
|
|
|
1061 |
|
|
=head1 get_modules_parameters_not_local
|
1062 |
|
|
|
1063 |
|
|
Gets the parameters for a module.
|
1064 |
|
|
|
1065 |
|
|
Arguments: - module name
|
1066 |
|
|
|
1067 |
|
|
Returns: - A hash (well, really a list that can be assigned to a hash).
|
1068 |
|
|
The keys of the hash are the parameters names. The values of the
|
1069 |
|
|
hash is everything (except comments) in the value.
|
1070 |
|
|
|
1071 |
|
|
Example: %parameters = $vdata->get_modules_parameters();
|
1072 |
|
|
foreach my $p (keys %parameters) { ...
|
1073 |
|
|
|
1074 |
|
|
=cut
|
1075 |
|
|
sub get_modules_parameters_not_local{
|
1076 |
|
|
my ($self,$module) = @_;
|
1077 |
|
|
|
1078 |
|
|
my %r;
|
1079 |
|
|
foreach my $p (keys %{$self->{modules}{$module}{parameters}}) {
|
1080 |
|
|
if ($self->{modules}{$module}{parameters}{$p}{ptype} ne "localparam"){
|
1081 |
|
|
#print "$p\n";
|
1082 |
|
|
$r{$p}=$self->{modules}{$module}{parameters}{$p}{value};
|
1083 |
|
|
}
|
1084 |
|
|
}
|
1085 |
|
|
return %r;
|
1086 |
|
|
}
|
1087 |
|
|
|
1088 |
|
|
|
1089 |
|
|
|
1090 |
|
|
|
1091 |
|
|
=head1 get_modules_parameters_not_local_in_order
|
1092 |
|
|
|
1093 |
|
|
Gets the parameter names in_order for a module.
|
1094 |
|
|
|
1095 |
|
|
|
1096 |
|
|
|
1097 |
|
|
=cut
|
1098 |
|
|
sub get_modules_parameters_not_local_order{
|
1099 |
|
|
my ($self,$module) = @_;
|
1100 |
|
|
my @r=@{$self->{modules}{$module}{parameter_order}};#param/localparam inorder
|
1101 |
|
|
my @w; #parameter inorder
|
1102 |
|
|
foreach my $p (@r) {
|
1103 |
|
|
if ($self->{modules}{$module}{parameters}{$p}{ptype} ne "localparam"){
|
1104 |
|
|
push(@w,$p);
|
1105 |
|
|
}
|
1106 |
|
|
|
1107 |
|
|
}
|
1108 |
|
|
|
1109 |
|
|
return @w;
|
1110 |
|
|
}
|
1111 |
|
|
|
1112 |
|
|
|
1113 |
|
|
|
1114 |
|
|
|
1115 |
|
|
=head1 get_module_ports_inorder
|
1116 |
|
|
|
1117 |
|
|
Gets the parameter names in_order for a module.
|
1118 |
|
|
|
1119 |
|
|
|
1120 |
|
|
|
1121 |
|
|
=cut
|
1122 |
|
|
sub get_module_ports_order{
|
1123 |
|
|
my ($self,$module) = @_;
|
1124 |
|
|
return @{$self->{modules}{$module}{port_order}};
|
1125 |
|
|
}
|
1126 |
|
|
|
1127 |
|
|
|
1128 |
|
|
|
1129 |
|
|
|
1130 |
|
|
|
1131 |
|
|
|
1132 |
|
|
###########################################################################
|
1133 |
|
|
|
1134 |
|
|
=head1 get_define
|
1135 |
|
|
|
1136 |
|
|
Find out where a define is defined and what the value is
|
1137 |
|
|
|
1138 |
|
|
Arguments: - name of the define
|
1139 |
|
|
Optional arguments where a you want the correct location and
|
1140 |
|
|
value for a particular use of a multiplely defined define:
|
1141 |
|
|
- file where define is used
|
1142 |
|
|
- line where define is used
|
1143 |
|
|
|
1144 |
|
|
Returns: - list with three elements: file, line, value
|
1145 |
|
|
or if the define does not exist it returns a empty list.
|
1146 |
|
|
if the define was defined on the command line it sets file=""
|
1147 |
|
|
and line=0
|
1148 |
|
|
|
1149 |
|
|
|
1150 |
|
|
Example: ($f,$l,$v) = $vdata->get_define($word,$file,$line);
|
1151 |
|
|
|
1152 |
|
|
=cut
|
1153 |
|
|
sub get_define {
|
1154 |
|
|
my ($self,$define,$file,$line) = @_;
|
1155 |
|
|
|
1156 |
|
|
if ( !defined($self) || !defined($define) ||
|
1157 |
|
|
( defined($file) && !defined($line) ) ) {
|
1158 |
|
|
die "Get define takes either two or four arguments";
|
1159 |
|
|
}
|
1160 |
|
|
|
1161 |
|
|
$define =~ s/^\`// ; # remove the ` if any
|
1162 |
|
|
|
1163 |
|
|
if (!exists( $self->{defines}{$define} )) {
|
1164 |
|
|
return ();
|
1165 |
|
|
}
|
1166 |
|
|
my $index = 0;
|
1167 |
|
|
my $dh = $self->{defines}{$define};
|
1168 |
|
|
|
1169 |
|
|
if (defined($file) &&
|
1170 |
|
|
exists($dh->{used}{$file}) &&
|
1171 |
|
|
exists($dh->{used}{$file}{$line})) {
|
1172 |
|
|
$index = $dh->{used}{$file}{$line};
|
1173 |
|
|
}
|
1174 |
|
|
|
1175 |
|
|
|
1176 |
|
|
if ($index eq "XX") { # define has been undefed
|
1177 |
|
|
return ();
|
1178 |
|
|
}
|
1179 |
|
|
|
1180 |
|
|
return ( $dh->{defined}[$index]{file},
|
1181 |
|
|
$dh->{defined}[$index]{line},
|
1182 |
|
|
$dh->{defined}[$index]{value});
|
1183 |
|
|
}
|
1184 |
|
|
|
1185 |
|
|
###########################################################################
|
1186 |
|
|
|
1187 |
|
|
=head1 get_context
|
1188 |
|
|
|
1189 |
|
|
Get the context (if any) for a line in a file.
|
1190 |
|
|
|
1191 |
|
|
Arguments: - file name
|
1192 |
|
|
- line number
|
1193 |
|
|
|
1194 |
|
|
Returns: - line number if there is a context, zero if there is none.
|
1195 |
|
|
|
1196 |
|
|
Example: $l = $vdata->get_context($filename,$line);
|
1197 |
|
|
|
1198 |
|
|
=cut
|
1199 |
|
|
sub get_context{
|
1200 |
|
|
my ($self,$file,$line) = @_;
|
1201 |
|
|
|
1202 |
|
|
if ( exists( $self->{files}{$file}{contexts}{$line} )) {
|
1203 |
|
|
return $line;
|
1204 |
|
|
}
|
1205 |
|
|
else {
|
1206 |
|
|
return 0;
|
1207 |
|
|
}
|
1208 |
|
|
}
|
1209 |
|
|
|
1210 |
|
|
###########################################################################
|
1211 |
|
|
|
1212 |
|
|
=head1 get_module_start_by_context
|
1213 |
|
|
|
1214 |
|
|
Test if the context is a module definition start.
|
1215 |
|
|
|
1216 |
|
|
Arguments: - file name
|
1217 |
|
|
- line number
|
1218 |
|
|
|
1219 |
|
|
Returns: - module name if it is a module start, 0 otherwise
|
1220 |
|
|
|
1221 |
|
|
Example: if($vdata->get_module_start_by_context($filename,$line))..
|
1222 |
|
|
|
1223 |
|
|
=cut
|
1224 |
|
|
# return true if the context for this line is a module start
|
1225 |
|
|
sub get_module_start_by_context{
|
1226 |
|
|
my ($self,$file,$line) = @_;
|
1227 |
|
|
|
1228 |
|
|
if ( exists( $self->{files}{$file}{contexts}{$line}{module_start})) {
|
1229 |
|
|
return $self->{files}{$file}{contexts}{$line}{module_start};
|
1230 |
|
|
}
|
1231 |
|
|
else {
|
1232 |
|
|
return 0;
|
1233 |
|
|
}
|
1234 |
|
|
}
|
1235 |
|
|
|
1236 |
|
|
###########################################################################
|
1237 |
|
|
|
1238 |
|
|
=head1 get_has_value_by_context
|
1239 |
|
|
|
1240 |
|
|
Check if the context has a value (ie a new module or something). Contexts
|
1241 |
|
|
that just turn on and off preprocessor ignoring do not have values.
|
1242 |
|
|
|
1243 |
|
|
Arguments: - file name
|
1244 |
|
|
- line number
|
1245 |
|
|
|
1246 |
|
|
Returns: - 1 if there is a value, 0 otherwise
|
1247 |
|
|
|
1248 |
|
|
Example: if ($vdata->get_has_value_by_context($file,$line))..
|
1249 |
|
|
|
1250 |
|
|
=cut
|
1251 |
|
|
sub get_has_value_by_context{
|
1252 |
|
|
my ($self,$file,$line) = @_;
|
1253 |
|
|
|
1254 |
|
|
return exists( $self->{files}{$file}{contexts}{$line}{value});
|
1255 |
|
|
}
|
1256 |
|
|
|
1257 |
|
|
###########################################################################
|
1258 |
|
|
|
1259 |
|
|
=head1 get_context_name_type
|
1260 |
|
|
|
1261 |
|
|
Find the reason for a new context - is it a module / function or task.
|
1262 |
|
|
Contexts that just turn on and off preprocessor ignoring do not have values.
|
1263 |
|
|
|
1264 |
|
|
Arguments: - file name
|
1265 |
|
|
- line number
|
1266 |
|
|
|
1267 |
|
|
Returns: - name
|
1268 |
|
|
- type [ module | function | task ]
|
1269 |
|
|
|
1270 |
|
|
Example: ($n,$t)=$vdata->get_context_name_type($file,$line);
|
1271 |
|
|
|
1272 |
|
|
=cut
|
1273 |
|
|
sub get_context_name_type{
|
1274 |
|
|
my ($self,$file,$line) = @_;
|
1275 |
|
|
my ($name,$type);
|
1276 |
|
|
|
1277 |
|
|
$type='';
|
1278 |
|
|
if (exists( $self->{files}{$file}{contexts}{$line}{value})) {
|
1279 |
|
|
$name= $self->{files}{$file}{contexts}{$line}{value}{name};
|
1280 |
|
|
if (exists( $self->{files}{$file}{contexts}{$line}{value}{type})) {
|
1281 |
|
|
$type=$self->{files}{$file}{contexts}{$line}{value}{type};
|
1282 |
|
|
$type='module' if ($type eq 'primitive' || $type eq 'macromodule');
|
1283 |
|
|
}
|
1284 |
|
|
return ($name,$type);
|
1285 |
|
|
}
|
1286 |
|
|
else {
|
1287 |
|
|
return ();
|
1288 |
|
|
}
|
1289 |
|
|
}
|
1290 |
|
|
|
1291 |
|
|
###########################################################################
|
1292 |
|
|
|
1293 |
|
|
=head1 get_pre_ignore_by_context
|
1294 |
|
|
|
1295 |
|
|
Test if the context is preprocessor ignore.
|
1296 |
|
|
|
1297 |
|
|
Arguments: - file name
|
1298 |
|
|
- line number
|
1299 |
|
|
|
1300 |
|
|
Returns: - 1 if it is, 0 otherwise
|
1301 |
|
|
|
1302 |
|
|
Example: if ($vdata->get_pre_ignore_by_context($file,$line))..
|
1303 |
|
|
|
1304 |
|
|
=cut
|
1305 |
|
|
sub get_pre_ignore_by_context{
|
1306 |
|
|
my ($self,$file,$line) = @_;
|
1307 |
|
|
|
1308 |
|
|
if (exists($self->{files}{$file}{contexts}{$line}{pre_ignore})) {
|
1309 |
|
|
return $self->{files}{$file}{contexts}{$line}{pre_ignore};
|
1310 |
|
|
}
|
1311 |
|
|
else {
|
1312 |
|
|
return 0;
|
1313 |
|
|
}
|
1314 |
|
|
|
1315 |
|
|
}
|
1316 |
|
|
|
1317 |
|
|
###########################################################################
|
1318 |
|
|
|
1319 |
|
|
=head1 get_first_instantiator_by_context
|
1320 |
|
|
|
1321 |
|
|
Get the first thing that instantiates this module using the context. The
|
1322 |
|
|
context must be a module_start.
|
1323 |
|
|
|
1324 |
|
|
Arguments: - file name (for context)
|
1325 |
|
|
- line name (for context)
|
1326 |
|
|
|
1327 |
|
|
Returns: - a 4 element list: instantiating module, file, instance name, line
|
1328 |
|
|
|
1329 |
|
|
Example:
|
1330 |
|
|
@i=$vdata->get_first_instantiator_by_context($f,$l );
|
1331 |
|
|
|
1332 |
|
|
=cut
|
1333 |
|
|
sub get_first_instantiator_by_context{
|
1334 |
|
|
my ($self,$file,$line) = @_;
|
1335 |
|
|
|
1336 |
|
|
# note: the second exists() checks that the module still exists as
|
1337 |
|
|
# it could have been deleted because a duplicate was found
|
1338 |
|
|
if (exists($self->{files}{$file}{contexts}{$line}{module_start}) &&
|
1339 |
|
|
exists($self->{modules}
|
1340 |
|
|
{$self->{files}{$file}{contexts}{$line}{module_start}}) &&
|
1341 |
|
|
exists($self->{files}{$file}{contexts}{$line}{value}{inst_by})) {
|
1342 |
|
|
$self->{current_instantiator} =0;
|
1343 |
|
|
$self->{current_instantiator_module}=
|
1344 |
|
|
$self->{files}{$file}{contexts}{$line}{module_start};
|
1345 |
|
|
return $self->get_next_instantiator();
|
1346 |
|
|
}
|
1347 |
|
|
else {
|
1348 |
|
|
return ();
|
1349 |
|
|
}
|
1350 |
|
|
|
1351 |
|
|
}
|
1352 |
|
|
|
1353 |
|
|
###########################################################################
|
1354 |
|
|
|
1355 |
|
|
=head1 get_inst_on_line
|
1356 |
|
|
|
1357 |
|
|
Gets the instance name of a line in a file
|
1358 |
|
|
|
1359 |
|
|
Arguments: - file name
|
1360 |
|
|
- line number
|
1361 |
|
|
|
1362 |
|
|
Returns: - name if the line has an instance name, 0 otherwise
|
1363 |
|
|
|
1364 |
|
|
Example: if ( $new_inst = $vdata->get_inst_on_line($file,$line) ) ...
|
1365 |
|
|
|
1366 |
|
|
=cut
|
1367 |
|
|
sub get_inst_on_line{
|
1368 |
|
|
my ($self,$file,$line) = @_;
|
1369 |
|
|
|
1370 |
|
|
if ( exists( $self->{files}{$file}{instance_lines}{$line})){
|
1371 |
|
|
return $self->{files}{$file}{instance_lines}{$line};
|
1372 |
|
|
}
|
1373 |
|
|
else {
|
1374 |
|
|
return 0;
|
1375 |
|
|
}
|
1376 |
|
|
}
|
1377 |
|
|
|
1378 |
|
|
###########################################################################
|
1379 |
|
|
|
1380 |
|
|
=head1 get_signal_by_context
|
1381 |
|
|
|
1382 |
|
|
Same as get_module_signal but works by specifying a context.
|
1383 |
|
|
|
1384 |
|
|
Arguments: - context file name
|
1385 |
|
|
- context line number
|
1386 |
|
|
- signal name
|
1387 |
|
|
|
1388 |
|
|
Returns: same as get_module_signal
|
1389 |
|
|
|
1390 |
|
|
Example:
|
1391 |
|
|
|
1392 |
|
|
=cut
|
1393 |
|
|
# get a signal by context - returns: line, a_line, i_line, type, file
|
1394 |
|
|
sub get_signal_by_context{
|
1395 |
|
|
my ($self,$file,$cline,$sig) = @_;
|
1396 |
|
|
|
1397 |
|
|
my $sigp;
|
1398 |
|
|
|
1399 |
|
|
# in tasks and functions signals can come from module (m_signals)
|
1400 |
|
|
# or from the task or function itself (which gets precedence).
|
1401 |
|
|
if (exists( $self->{files}{$file}{contexts}{$cline}{value}{signals}{$sig} )) {
|
1402 |
|
|
print " found signal $sig\n" if $debug;
|
1403 |
|
|
$sigp=$self->{files}{$file}{contexts}{$cline}{value}{signals}{$sig};
|
1404 |
|
|
}
|
1405 |
|
|
elsif (exists( $self->{files}{$file}{contexts}{$cline}{value}{m_signals}{$sig} )) {
|
1406 |
|
|
print " found m_signal $sig\n" if $debug;
|
1407 |
|
|
$sigp=$self->{files}{$file}{contexts}{$cline}{value}{m_signals}{$sig};
|
1408 |
|
|
}
|
1409 |
|
|
else {
|
1410 |
|
|
return ();
|
1411 |
|
|
}
|
1412 |
|
|
|
1413 |
|
|
return ($sigp->{line},
|
1414 |
|
|
$sigp->{a_line},
|
1415 |
|
|
$sigp->{i_line},
|
1416 |
|
|
$sigp->{type},
|
1417 |
|
|
$sigp->{file},
|
1418 |
|
|
$sigp->{posedge},
|
1419 |
|
|
$sigp->{negedge},
|
1420 |
|
|
$sigp->{type2},
|
1421 |
|
|
$sigp->{source}{file},
|
1422 |
|
|
$sigp->{source}{line},
|
1423 |
|
|
$sigp->{range},
|
1424 |
|
|
$sigp->{a_file},
|
1425 |
|
|
$sigp->{i_file},
|
1426 |
|
|
$sigp->{dimensions});
|
1427 |
|
|
}
|
1428 |
|
|
|
1429 |
|
|
###########################################################################
|
1430 |
|
|
|
1431 |
|
|
=head1 get_t_or_f_by_context
|
1432 |
|
|
|
1433 |
|
|
Same as get_modules_t_or_f but works by specifying a context.
|
1434 |
|
|
|
1435 |
|
|
Arguments: - context file name
|
1436 |
|
|
- context line number
|
1437 |
|
|
- task name
|
1438 |
|
|
|
1439 |
|
|
Returns: - same as get_modules_t_or_f
|
1440 |
|
|
|
1441 |
|
|
Example:
|
1442 |
|
|
|
1443 |
|
|
=cut
|
1444 |
|
|
sub get_t_or_f_by_context{
|
1445 |
|
|
my ($self,$cfile,$cline,$t_or_f) = @_;
|
1446 |
|
|
|
1447 |
|
|
if (exists($self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f})) {
|
1448 |
|
|
return($self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{type},
|
1449 |
|
|
$self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{line},
|
1450 |
|
|
$self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{file},
|
1451 |
|
|
$self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{anchor});
|
1452 |
|
|
}
|
1453 |
|
|
else {
|
1454 |
|
|
return ();
|
1455 |
|
|
}
|
1456 |
|
|
}
|
1457 |
|
|
###########################################################################
|
1458 |
|
|
|
1459 |
|
|
=head1 get_parameter_by_context
|
1460 |
|
|
|
1461 |
|
|
Return the file and line for a named parameter using context
|
1462 |
|
|
|
1463 |
|
|
Arguments: - context file name
|
1464 |
|
|
- context line number
|
1465 |
|
|
- parameter name
|
1466 |
|
|
|
1467 |
|
|
Returns: - file and line of definition
|
1468 |
|
|
|
1469 |
|
|
Example:
|
1470 |
|
|
|
1471 |
|
|
=cut
|
1472 |
|
|
sub get_parameter_by_context{
|
1473 |
|
|
my ($self,$cfile,$cline,$parameter) = @_;
|
1474 |
|
|
|
1475 |
|
|
if (exists($self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter})) {
|
1476 |
|
|
return($self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter}{file},
|
1477 |
|
|
$self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter}{line});
|
1478 |
|
|
}
|
1479 |
|
|
else {
|
1480 |
|
|
return ();
|
1481 |
|
|
}
|
1482 |
|
|
}
|
1483 |
|
|
###########################################################################
|
1484 |
|
|
|
1485 |
|
|
=head1 get_anchors
|
1486 |
|
|
|
1487 |
|
|
Get the anchors for a line in a file.
|
1488 |
|
|
|
1489 |
|
|
|
1490 |
|
|
Returns: - a list of anchors
|
1491 |
|
|
|
1492 |
|
|
Example: foreach $anchor ( $vdata->get_anchors($file,$line) ) ..
|
1493 |
|
|
|
1494 |
|
|
=cut
|
1495 |
|
|
sub get_anchors{
|
1496 |
|
|
my ($self,$file,$line) = @_;
|
1497 |
|
|
|
1498 |
|
|
if (exists($self->{files}{$file}{anchors}{$line})) {
|
1499 |
|
|
return @{$self->{files}{$file}{anchors}{$line}};
|
1500 |
|
|
}
|
1501 |
|
|
else {
|
1502 |
|
|
return ();
|
1503 |
|
|
}
|
1504 |
|
|
}
|
1505 |
|
|
|
1506 |
|
|
###########################################################################
|
1507 |
|
|
|
1508 |
|
|
=head1 expand_defines
|
1509 |
|
|
|
1510 |
|
|
Expand the defines in a line of verilog code. for best use this
|
1511 |
|
|
should be called line by line, so that the defines get the correct
|
1512 |
|
|
values when defines are defined multiple times
|
1513 |
|
|
|
1514 |
|
|
Arguments: - a pointer to the string to expand the defines in
|
1515 |
|
|
- the file the line is from
|
1516 |
|
|
- the line number of the line
|
1517 |
|
|
|
1518 |
|
|
Returns: - nothing
|
1519 |
|
|
|
1520 |
|
|
Example: $vdata->expand_defines(\$line_to_expand,$file,$line);
|
1521 |
|
|
|
1522 |
|
|
=cut
|
1523 |
|
|
###############################################################################
|
1524 |
|
|
#
|
1525 |
|
|
sub expand_defines {
|
1526 |
|
|
my ($self,$bufp,$file,$line) = @_;
|
1527 |
|
|
|
1528 |
|
|
if (exists($self->{files}{$file}{define_lines}{$line})) {
|
1529 |
|
|
# do not expand on a `define line - it doesn't make sense to do so
|
1530 |
|
|
# as the substitution of defines in the value only occurs at the
|
1531 |
|
|
# time of use when they could have different values!
|
1532 |
|
|
return;
|
1533 |
|
|
}
|
1534 |
|
|
|
1535 |
|
|
|
1536 |
|
|
while ( $$bufp =~ m/^(.*?)\`($VID)/ ) {
|
1537 |
|
|
my $b = $1;
|
1538 |
|
|
my $d = $2;
|
1539 |
|
|
my $dq = quotemeta($d);
|
1540 |
|
|
my $v;
|
1541 |
|
|
if ((undef,undef,$v) = $self->get_define($d,$file,$line)) {
|
1542 |
|
|
$$bufp =~ s/\`$dq/$v/;
|
1543 |
|
|
}
|
1544 |
|
|
else {
|
1545 |
|
|
$$bufp =~ s/\`$dq/_BaCkQuOtE_$dq/;
|
1546 |
|
|
}
|
1547 |
|
|
|
1548 |
|
|
}
|
1549 |
|
|
$$bufp =~ s/_BaCkQuOtE_/\`/g;
|
1550 |
|
|
}
|
1551 |
|
|
|
1552 |
|
|
|
1553 |
|
|
###########################################################################
|
1554 |
|
|
|
1555 |
|
|
=head1 verilog_gatetype_keywords
|
1556 |
|
|
|
1557 |
|
|
|
1558 |
|
|
Returns: - a list of verilog gatetype keywords
|
1559 |
|
|
|
1560 |
|
|
Example: @keywords = rvp->verilog_gatetype_keywords();
|
1561 |
|
|
|
1562 |
|
|
=cut
|
1563 |
|
|
sub verilog_gatetype_keywords {
|
1564 |
|
|
return (@verilog_gatetype_keywords);
|
1565 |
|
|
}
|
1566 |
|
|
###########################################################################
|
1567 |
|
|
|
1568 |
|
|
=head1 verilog_compiler_keywords
|
1569 |
|
|
|
1570 |
|
|
Returns: - a list of verilog compiler keywords
|
1571 |
|
|
|
1572 |
|
|
Example: @keywords = rvp->verilog_compiler_keywords();
|
1573 |
|
|
|
1574 |
|
|
=cut
|
1575 |
|
|
sub verilog_compiler_keywords {
|
1576 |
|
|
return (@verilog_compiler_keywords);
|
1577 |
|
|
}
|
1578 |
|
|
###########################################################################
|
1579 |
|
|
|
1580 |
|
|
=head1 verilog_signal_keywords
|
1581 |
|
|
|
1582 |
|
|
Returns: - a list of verilog signal keywords
|
1583 |
|
|
|
1584 |
|
|
Example: @keywords = rvp->verilog_signal_keywords();
|
1585 |
|
|
|
1586 |
|
|
=cut
|
1587 |
|
|
sub verilog_signal_keywords {
|
1588 |
|
|
return (@verilog_signal_keywords);
|
1589 |
|
|
}
|
1590 |
|
|
|
1591 |
|
|
|
1592 |
|
|
###########################################################################
|
1593 |
|
|
|
1594 |
|
|
=head1 chunk_read_init
|
1595 |
|
|
|
1596 |
|
|
Initialise a file for chunk reading (see chunk_read for more
|
1597 |
|
|
details). It actually reads the whole file into a string, which
|
1598 |
|
|
chunk_read then reads a chunk at a time. The file is closed before
|
1599 |
|
|
chuck_read_init returns.
|
1600 |
|
|
|
1601 |
|
|
Arguments: - the file to read (with path if needed)
|
1602 |
|
|
- tabstop: 0 = leave tabs alone
|
1603 |
|
|
N = turn tabs spaces with each tabstop=N
|
1604 |
|
|
Returns: - a handle to pass to chunk_read or 0 if file open fails
|
1605 |
|
|
|
1606 |
|
|
Example:
|
1607 |
|
|
my $chunkRead = rvp->chunkr_read_init($f,$opts{tabstop});
|
1608 |
|
|
|
1609 |
|
|
=cut
|
1610 |
|
|
sub chunk_read_init {
|
1611 |
|
|
|
1612 |
|
|
my ($class,$f,$tabstop) = @_;
|
1613 |
|
|
local (*F);
|
1614 |
|
|
|
1615 |
|
|
open(F,"<$f") || return 0;
|
1616 |
|
|
|
1617 |
|
|
my $chunk = { type => "",
|
1618 |
|
|
text => "",
|
1619 |
|
|
isANewLine => 0,
|
1620 |
|
|
isStart => 0,
|
1621 |
|
|
isEnd => 1 ,
|
1622 |
|
|
line => 0 };
|
1623 |
|
|
|
1624 |
|
|
my $this = { chunk => $chunk ,
|
1625 |
|
|
tabstop => $tabstop ,
|
1626 |
|
|
linebuf => "" ,
|
1627 |
|
|
state => 0 ,
|
1628 |
|
|
fh => *F };
|
1629 |
|
|
return $this;
|
1630 |
|
|
}
|
1631 |
|
|
|
1632 |
|
|
###########################################################################
|
1633 |
|
|
|
1634 |
|
|
=head1 chunk_read
|
1635 |
|
|
|
1636 |
|
|
Reads verilog a chunk at a time. The file is opened using
|
1637 |
|
|
chunk_read_init. Then chunk_read is used to read the file a chunk at a
|
1638 |
|
|
time. A chunk is a line or part of a line that is all the same type.
|
1639 |
|
|
|
1640 |
|
|
The types are:
|
1641 |
|
|
comment - either // or /* */ comment
|
1642 |
|
|
attribute - verilog 2001 (* *) atribute
|
1643 |
|
|
include - a line containing `include "file"
|
1644 |
|
|
string - a string
|
1645 |
|
|
code - anything else (verilog code, defines, compliler keywords)
|
1646 |
|
|
|
1647 |
|
|
Nothing is removed from the file, so if each chunk is printed after being read
|
1648 |
|
|
you will end up with exactly the same file as you put in.
|
1649 |
|
|
|
1650 |
|
|
Arguments: - handle (from chunk_read_init)
|
1651 |
|
|
|
1652 |
|
|
Returns: - 0 at the end of file, or a hash ref with the following keys:
|
1653 |
|
|
type - one of the types (see above)
|
1654 |
|
|
text - the text read from the file
|
1655 |
|
|
line - the line number the text is on
|
1656 |
|
|
isANewLine - true if chunk is the first chunk of the line
|
1657 |
|
|
isStart - true if the chunk is the start (eg "/*..." for
|
1658 |
|
|
a comment )
|
1659 |
|
|
isEnd - true if the chunk is the end ( eg "*/" )
|
1660 |
|
|
NOTE: isEnd is set to undefined for a
|
1661 |
|
|
type="code" that ends in a newline. This is
|
1662 |
|
|
because chunk_read doesn't know if the code is
|
1663 |
|
|
ending or not. If you need to know in this case
|
1664 |
|
|
you can read the next chunk and see what type it is.
|
1665 |
|
|
|
1666 |
|
|
Example:
|
1667 |
|
|
my $chunkRead = rvp->chunk_read_init($f,0);
|
1668 |
|
|
while ($chunk = rvp->chunk_read($chunkRead)) {
|
1669 |
|
|
print $chunk->{text} unless $chunk->{type} eq "comment";
|
1670 |
|
|
}
|
1671 |
|
|
|
1672 |
|
|
=cut
|
1673 |
|
|
sub chunk_read {
|
1674 |
|
|
my ($class,$this) = @_;
|
1675 |
|
|
|
1676 |
|
|
my $chunk = $this->{chunk};
|
1677 |
|
|
$chunk->{isStart} = $chunk->{isEnd};
|
1678 |
|
|
$chunk->{isEnd} = 0;
|
1679 |
|
|
$chunk->{isANewLine} = 0;
|
1680 |
|
|
|
1681 |
|
|
if ( $this->{linebuf} eq "" ) {
|
1682 |
|
|
if (!defined($this->{linebuf} = readline($this->{fh}))) {
|
1683 |
|
|
close($this->{fh});
|
1684 |
|
|
return 0;
|
1685 |
|
|
}
|
1686 |
|
|
$chunk->{isANewLine} = 1;
|
1687 |
|
|
$chunk->{line}++;
|
1688 |
|
|
if ($this->{tabstop}!=0) {
|
1689 |
|
|
# 1 while is some stupid perl thing meaning while (cond) {} may be a bit faster?
|
1690 |
|
|
1 while ($this->{linebuf} =~ s/(^|\n)([^\t\n]*)(\t+)/
|
1691 |
|
|
$1. $2 . (" " x ($this->{tabstop} * length($3) -
|
1692 |
|
|
(length($2) % $this->{tabstop})))
|
1693 |
|
|
/gsex);
|
1694 |
|
|
}
|
1695 |
|
|
}
|
1696 |
|
|
|
1697 |
|
|
STATE_SWITCH:
|
1698 |
|
|
if ( $this->{state} == 0 ) {
|
1699 |
|
|
$chunk->{type} = "code";
|
1700 |
|
|
if ( $this->{linebuf} =~
|
1701 |
|
|
s%^(.*?)((/\*)| # anything followed by /* comment
|
1702 |
|
|
(//)| # or // comment
|
1703 |
|
|
(\(\*(?!\s*\)))| # or (* attribute (but not (*)
|
1704 |
|
|
(\`include\s)| # or `include
|
1705 |
|
|
(\")) # or start of string
|
1706 |
|
|
%$2%ox ) {
|
1707 |
|
|
$chunk->{isEnd} = 1;
|
1708 |
|
|
$chunk->{text} = $1;
|
1709 |
|
|
if (defined($3)) {
|
1710 |
|
|
$this->{state} = 1; # long comment
|
1711 |
|
|
}
|
1712 |
|
|
elsif (defined($4)) {
|
1713 |
|
|
$this->{state} = 2; # short comment
|
1714 |
|
|
}
|
1715 |
|
|
elsif (defined($5)) {
|
1716 |
|
|
$this->{state} = 3; # attribute
|
1717 |
|
|
}
|
1718 |
|
|
elsif (defined($6)) {
|
1719 |
|
|
$this->{state} = 4; # include
|
1720 |
|
|
}
|
1721 |
|
|
elsif (defined($7)) {
|
1722 |
|
|
$this->{state} = 5; # string
|
1723 |
|
|
}
|
1724 |
|
|
else {
|
1725 |
|
|
die "chunk_read internal error!";
|
1726 |
|
|
}
|
1727 |
|
|
if (!$chunk->{text}) {
|
1728 |
|
|
# this happens if we are in state code and a new line
|
1729 |
|
|
# starts with something that isn't code. So we change
|
1730 |
|
|
# and go back to the top.
|
1731 |
|
|
$chunk->{isStart} = 1;
|
1732 |
|
|
$chunk->{isEnd} = 0;
|
1733 |
|
|
goto STATE_SWITCH;
|
1734 |
|
|
}
|
1735 |
|
|
}
|
1736 |
|
|
else {
|
1737 |
|
|
$chunk->{text} = $this->{linebuf};
|
1738 |
|
|
$this->{linebuf} = "";
|
1739 |
|
|
# in this case we might be at end, but we don't really know!
|
1740 |
|
|
$chunk->{isEnd} = undef;
|
1741 |
|
|
}
|
1742 |
|
|
}
|
1743 |
|
|
elsif ( $this->{state} == 1 ) {
|
1744 |
|
|
$chunk->{type} = "comment";
|
1745 |
|
|
# this first test is needed to work so /*/ */ works
|
1746 |
|
|
if ( $chunk->{isStart} && $this->{linebuf} =~ s%^/\*%% ) {
|
1747 |
|
|
$chunk->{text} = "/*";
|
1748 |
|
|
}
|
1749 |
|
|
else {
|
1750 |
|
|
$chunk->{text} = "";
|
1751 |
|
|
}
|
1752 |
|
|
if ( $this->{linebuf} =~ s%^(.*?\*/)%% ) { # anything followed by */
|
1753 |
|
|
$chunk->{text} .= $1;
|
1754 |
|
|
$this->{state} = 0;
|
1755 |
|
|
$chunk->{isEnd} = 1;
|
1756 |
|
|
}
|
1757 |
|
|
else {
|
1758 |
|
|
$chunk->{text} .= $this->{linebuf};
|
1759 |
|
|
$this->{linebuf} = "";
|
1760 |
|
|
}
|
1761 |
|
|
}
|
1762 |
|
|
elsif ( $this->{state} == 2 ) {
|
1763 |
|
|
$chunk->{type} = "comment";
|
1764 |
|
|
$chunk->{text} = $this->{linebuf};
|
1765 |
|
|
$chunk->{isEnd} = 1;
|
1766 |
|
|
$this->{linebuf} = "";
|
1767 |
|
|
if ( $chunk->{text} =~ s/\n$// ) {
|
1768 |
|
|
$this->{linebuf} = "\n";
|
1769 |
|
|
}
|
1770 |
|
|
$this->{state} = 0;
|
1771 |
|
|
}
|
1772 |
|
|
elsif ( $this->{state} == 3 ) {
|
1773 |
|
|
$chunk->{type} = "attribute";
|
1774 |
|
|
if ( $this->{linebuf} =~ s%^(.*?\*\))%% ) { # anything followed by *)
|
1775 |
|
|
$chunk->{text} = $1;
|
1776 |
|
|
$this->{state} = 0;
|
1777 |
|
|
$chunk->{isEnd} = 1;
|
1778 |
|
|
}
|
1779 |
|
|
else {
|
1780 |
|
|
$chunk->{text} = $this->{linebuf};
|
1781 |
|
|
$this->{linebuf} = "";
|
1782 |
|
|
}
|
1783 |
|
|
}
|
1784 |
|
|
elsif ( $this->{state} == 4 ) {
|
1785 |
|
|
$chunk->{type} = "include";
|
1786 |
|
|
$chunk->{isEnd} = 1;
|
1787 |
|
|
if ( $this->{linebuf} =~ s%^(\`include\s+\".*?\")%% ) {
|
1788 |
|
|
$chunk->{text} = $1;
|
1789 |
|
|
$this->{state} = 0;
|
1790 |
|
|
}
|
1791 |
|
|
else {
|
1792 |
|
|
# this is an error - just return the line as code - the parser will
|
1793 |
|
|
# report the error
|
1794 |
|
|
$chunk->{type} = 0;
|
1795 |
|
|
$chunk->{text} = $this->{linebuf};
|
1796 |
|
|
$this->{linebuf} = "";
|
1797 |
|
|
}
|
1798 |
|
|
}
|
1799 |
|
|
elsif ( $this->{state} == 5 ) {
|
1800 |
|
|
$chunk->{type} = "string";
|
1801 |
|
|
# string all on one line
|
1802 |
|
|
if ( $this->{linebuf} =~ s%^(\"(?:(?:\\\\)|(?:\\\")|(?:[^\"]))*?\")%% ) {
|
1803 |
|
|
$chunk->{text} = $1;
|
1804 |
|
|
$this->{state} = 0;
|
1805 |
|
|
$chunk->{isEnd} = 1;
|
1806 |
|
|
}
|
1807 |
|
|
# end of multiline string (doesn't start with quote)
|
1808 |
|
|
elsif ( $this->{linebuf} =~ s%^([^\"](?:(?:\\\\)|(?:\\\")|(?:[^\"]))*?\")%% ) {
|
1809 |
|
|
$chunk->{text} = $1;
|
1810 |
|
|
$this->{state} = 0;
|
1811 |
|
|
$chunk->{isEnd} = 1;
|
1812 |
|
|
}
|
1813 |
|
|
# middle of multiline string
|
1814 |
|
|
else {
|
1815 |
|
|
$chunk->{text} = $this->{linebuf};
|
1816 |
|
|
$this->{linebuf} = "";
|
1817 |
|
|
}
|
1818 |
|
|
}
|
1819 |
|
|
|
1820 |
|
|
return $chunk;
|
1821 |
|
|
}
|
1822 |
|
|
|
1823 |
|
|
###############################################################################
|
1824 |
|
|
# RVP internal functions from now on.... (they all start with _ to
|
1825 |
|
|
# let you know they are internal
|
1826 |
|
|
###############################################################################
|
1827 |
|
|
|
1828 |
|
|
###############################################################################
|
1829 |
|
|
# search a file, putting the data in $self
|
1830 |
|
|
# Note: be careful coding in the main loop... there are a few optimisations
|
1831 |
|
|
# which result in big chunks of code being skipped if the line does not
|
1832 |
|
|
# contain certain characters (eg ' " / *)
|
1833 |
|
|
sub _search {
|
1834 |
|
|
my ($self,$f,$inc_dirs) = @_;
|
1835 |
|
|
|
1836 |
|
|
my $verilog_compiler_keywords_regexp = "(?:" .
|
1837 |
|
|
join("|",@verilog_compiler_keywords) .
|
1838 |
|
|
")";
|
1839 |
|
|
|
1840 |
|
|
|
1841 |
|
|
my $file=_ffile($f);
|
1842 |
|
|
_init_file($self->{files},$f);
|
1843 |
|
|
|
1844 |
|
|
print "Searching $f " unless $quiet;
|
1845 |
|
|
my $chunkRead= rvp->chunk_read_init($f,0) ||
|
1846 |
|
|
die "Error: can not open file $f to read: $!\n";
|
1847 |
|
|
my $file_dir = dirname($f);
|
1848 |
|
|
|
1849 |
|
|
my $rs = {};
|
1850 |
|
|
$rs->{modules} = $self->{modules};
|
1851 |
|
|
$rs->{files} = $self->{files};
|
1852 |
|
|
$rs->{unres_mod} = $self->{unresolved_modules};
|
1853 |
|
|
|
1854 |
|
|
$rs->{module} = '';
|
1855 |
|
|
$rs->{function} = '';
|
1856 |
|
|
$rs->{task} = '';
|
1857 |
|
|
$rs->{t} = undef; # temp store
|
1858 |
|
|
$rs->{p} = undef;
|
1859 |
|
|
|
1860 |
|
|
my $printline = 1000;
|
1861 |
|
|
|
1862 |
|
|
my $ps = {};
|
1863 |
|
|
my $nest=0;
|
1864 |
|
|
my $nest_at_ignore;
|
1865 |
|
|
my @ignore_from_elsif;
|
1866 |
|
|
my $ignoring=0;
|
1867 |
|
|
my @fileStack =();
|
1868 |
|
|
my $pp_ignore;
|
1869 |
|
|
my $chunk;
|
1870 |
|
|
while (1) {
|
1871 |
|
|
while ($chunk = rvp->chunk_read($chunkRead)) {
|
1872 |
|
|
$self->{files}{$file}{lines} = $chunk->{line};
|
1873 |
|
|
if ($chunk->{line}>$printline && !$quiet) {
|
1874 |
|
|
$printline+=1000;
|
1875 |
|
|
$|=1; # turn on autoflush
|
1876 |
|
|
print ".";
|
1877 |
|
|
$|=0 unless $debug; # turn off autoflush
|
1878 |
|
|
}
|
1879 |
|
|
|
1880 |
|
|
# deal quickly with blank lines
|
1881 |
|
|
if ( $chunk->{text} =~ m/^\s*\n/ ) {
|
1882 |
|
|
next;
|
1883 |
|
|
}
|
1884 |
|
|
|
1885 |
|
|
|
1886 |
|
|
if ( $chunk->{type} eq "code" ) {
|
1887 |
|
|
|
1888 |
|
|
|
1889 |
|
|
####################################################
|
1890 |
|
|
# Optimisation: if there are no `
|
1891 |
|
|
# we can parse the line now
|
1892 |
|
|
if ( $chunk->{text} !~ m|[\`]| ) {
|
1893 |
|
|
if ($nest && $ignoring) {
|
1894 |
|
|
next;
|
1895 |
|
|
}
|
1896 |
|
|
$self->_parse_line($chunk->{text},$file,$chunk->{line},$ps,$rs);
|
1897 |
|
|
next;
|
1898 |
|
|
}
|
1899 |
|
|
|
1900 |
|
|
# handle ifdefs
|
1901 |
|
|
if ($nest && $ignoring) {
|
1902 |
|
|
if ( $chunk->{text} =~ m/^\s*\`(?:ifdef|ifndef)\s+($VID)/ ) {
|
1903 |
|
|
print " Found at line $chunk->{line} : if[n]def (nest=$nest)\n" if $debug;
|
1904 |
|
|
$nest++;
|
1905 |
|
|
}
|
1906 |
|
|
elsif ( $chunk->{text} =~ m/^\s*\`(else|(?:elsif\s+($VID)))/ ) {
|
1907 |
|
|
print " Found at line $chunk->{line} : $1 (nest=$nest)\n" if $debug;
|
1908 |
|
|
if ($1 eq 'else' ||
|
1909 |
|
|
_parsing_is_defined($self->{defines},$2,
|
1910 |
|
|
$file,$chunk->{line})) {
|
1911 |
|
|
# true elsif or plain else
|
1912 |
|
|
if ($nest == $nest_at_ignore &&
|
1913 |
|
|
!$ignore_from_elsif[$nest]) {
|
1914 |
|
|
$ignoring=0;
|
1915 |
|
|
$$pp_ignore = $chunk->{line};
|
1916 |
|
|
}
|
1917 |
|
|
}
|
1918 |
|
|
}
|
1919 |
|
|
elsif ( $chunk->{text} =~ m/^\s*\`endif/ ) {
|
1920 |
|
|
print " Found at line $chunk->{line} : endif (nest=$nest)\n" if $debug;
|
1921 |
|
|
if ($nest == $nest_at_ignore) {
|
1922 |
|
|
$ignoring=0;
|
1923 |
|
|
$$pp_ignore = $chunk->{line};
|
1924 |
|
|
}
|
1925 |
|
|
$nest--;
|
1926 |
|
|
}
|
1927 |
|
|
next;
|
1928 |
|
|
}
|
1929 |
|
|
# handle the case where the endif is on the same line as the ifdef
|
1930 |
|
|
# (note: generally I only accept endif at the start of a line)
|
1931 |
|
|
if ( $chunk->{text} =~ m/\`(ifdef|ifndef)\s+($VID).*\`endif/ ) {
|
1932 |
|
|
print "$file: ifdef and endif on same line\n" if $debug;
|
1933 |
|
|
my $is_defined = _parsing_is_defined($self->{defines},$2,
|
1934 |
|
|
$file,$chunk->{line});
|
1935 |
|
|
if ( (($1 eq 'ifdef' ) && !$is_defined) ||
|
1936 |
|
|
(($1 eq 'ifndef') && $is_defined)) {
|
1937 |
|
|
# replace ifdef with nothing
|
1938 |
|
|
$chunk->{text} =~ s/\`(ifdef|ifndef)\s+($VID)(.*)\`endif//;
|
1939 |
|
|
}
|
1940 |
|
|
else {
|
1941 |
|
|
# replace ifdef with what is between the ifdef and endif
|
1942 |
|
|
$chunk->{text} =~ s/\`(ifdef|ifndef)\s+($VID)(.*)\`endif/$3/;
|
1943 |
|
|
}
|
1944 |
|
|
}
|
1945 |
|
|
if ( $chunk->{text} =~ m/^\s*\`(ifdef|ifndef)\s+($VID)/ ) {
|
1946 |
|
|
$nest++;
|
1947 |
|
|
print " Found at line $chunk->{line} : $1 $2 (nest=$nest)\n" if $debug;
|
1948 |
|
|
my $is_defined = _parsing_is_defined($self->{defines},$2,
|
1949 |
|
|
$file,$chunk->{line});
|
1950 |
|
|
if ( (($1 eq 'ifdef' ) && !$is_defined) ||
|
1951 |
|
|
(($1 eq 'ifndef') && $is_defined)) {
|
1952 |
|
|
$ignoring=1;
|
1953 |
|
|
$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore} = 'XX';
|
1954 |
|
|
$pp_ignore = \$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore};
|
1955 |
|
|
$nest_at_ignore=$nest;
|
1956 |
|
|
$ignore_from_elsif[$nest]=0;
|
1957 |
|
|
}
|
1958 |
|
|
next;
|
1959 |
|
|
}
|
1960 |
|
|
if ( $chunk->{text} =~ m/^\s*\`(else|(?:elsif\s+($VID)))/ ) {
|
1961 |
|
|
print " Found at line $chunk->{line} : $1 (nest=$nest)\n" if $debug;
|
1962 |
|
|
if ($nest) {
|
1963 |
|
|
$ignoring=1;
|
1964 |
|
|
$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore} = 'XX';
|
1965 |
|
|
$pp_ignore = \$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore};
|
1966 |
|
|
$nest_at_ignore=$nest;
|
1967 |
|
|
# an ignore from an elsif means you will never stop ignoring
|
1968 |
|
|
# at this nest level
|
1969 |
|
|
$ignore_from_elsif[$nest]=($1 ne 'else');
|
1970 |
|
|
}
|
1971 |
|
|
else {
|
1972 |
|
|
$self->_add_warning("$file:$chunk->{line}: found $1 without \`ifdef");
|
1973 |
|
|
}
|
1974 |
|
|
next;
|
1975 |
|
|
}
|
1976 |
|
|
if ( $chunk->{text} =~ m/^\s*\`endif/ ) {
|
1977 |
|
|
print " Found at line $chunk->{line} : endif (nest=$nest)\n" if $debug;
|
1978 |
|
|
if ($nest) {
|
1979 |
|
|
$nest--;
|
1980 |
|
|
}
|
1981 |
|
|
else {
|
1982 |
|
|
$self->_add_warning("$file:$chunk->{line}: found \`endif without \`ifdef");
|
1983 |
|
|
}
|
1984 |
|
|
next;
|
1985 |
|
|
}
|
1986 |
|
|
|
1987 |
|
|
# match define. Note: /s makes the .* match the \n too
|
1988 |
|
|
if ( $chunk->{text} =~ m/^\s*\`define\s+($VID)(.*)/s ) {
|
1989 |
|
|
my $def = $1;
|
1990 |
|
|
my $rest = defined($2)?$2:'';
|
1991 |
|
|
my $defLine = $chunk->{line};
|
1992 |
|
|
$self->{files}{$file}{define_lines}{$chunk->{line}} = 1;
|
1993 |
|
|
|
1994 |
|
|
# _parsing_expand_defines is called to register the use
|
1995 |
|
|
# of any multiplely defined defines in the value part of
|
1996 |
|
|
# the define
|
1997 |
|
|
my $tmpValue=$rest;
|
1998 |
|
|
$self->_parsing_expand_defines(\$tmpValue,$file,$chunk->{line});
|
1999 |
|
|
|
2000 |
|
|
# handle multiline defines: read more stuff if line ends in backslash
|
2001 |
|
|
# (revisit: verilog spec says leave the newline in the value)
|
2002 |
|
|
# also keep adding stuff to value until it ends in a newline or comment
|
2003 |
|
|
# because strings are seperated out, `define T $display("test")
|
2004 |
|
|
# is delivered as chunks '`define T $display(' ,'"test"', ')\n'
|
2005 |
|
|
while ( (($rest =~ s|\\\n|| ) || ($rest !~ m/\n$/) )
|
2006 |
|
|
&& ($chunk = rvp->chunk_read($chunkRead))) {
|
2007 |
|
|
last if $chunk->{type} eq "comment";
|
2008 |
|
|
$rest .= $chunk->{text};
|
2009 |
|
|
$self->{files}{$file}{define_lines}{$chunk->{line}} = 1;
|
2010 |
|
|
# _parsing_expand_defines call: see comment ~15 lines back
|
2011 |
|
|
my $tmpValue=$chunk->{text};
|
2012 |
|
|
$self->_parsing_expand_defines(\$tmpValue,$file,$chunk->{line});
|
2013 |
|
|
}
|
2014 |
|
|
my $value = $rest;
|
2015 |
|
|
$value =~ s/^\s+(.*)(\n)?/$1/;
|
2016 |
|
|
|
2017 |
|
|
print " Found in $file line $defLine : define $def = $value\n"
|
2018 |
|
|
if $debug;
|
2019 |
|
|
_add_define($self->{defines}, $def , $value , $file, $defLine );
|
2020 |
|
|
_add_anchor($self->{files}{$file}{anchors},$defLine,"");
|
2021 |
|
|
# Don't substitute now: [defines] shall be substituted after the
|
2022 |
|
|
# original macro is substituted, not when it is defined(1364-2001 pg353)
|
2023 |
|
|
next;
|
2024 |
|
|
}
|
2025 |
|
|
|
2026 |
|
|
if ( $chunk->{text} =~ m/^\s*\`undef\s+($VID)/ ) {
|
2027 |
|
|
_undef_define($self->{defines},$1);
|
2028 |
|
|
print " Found at line $chunk->{line} : undef $1\n" if $debug;
|
2029 |
|
|
next;
|
2030 |
|
|
}
|
2031 |
|
|
|
2032 |
|
|
if ( $chunk->{text} =~ m/^\s*$verilog_compiler_keywords_regexp/ ) {
|
2033 |
|
|
next;
|
2034 |
|
|
}
|
2035 |
|
|
$self->_parsing_expand_defines(\$chunk->{text},$file,$chunk->{line});
|
2036 |
|
|
|
2037 |
|
|
# Note this is called from two other places (optimisations)
|
2038 |
|
|
$self->_parse_line($chunk->{text},$file,$chunk->{line},$ps,$rs);
|
2039 |
|
|
}
|
2040 |
|
|
elsif ( $chunk->{type} eq "include" ) {
|
2041 |
|
|
if ($nest && $ignoring) {
|
2042 |
|
|
next;
|
2043 |
|
|
}
|
2044 |
|
|
|
2045 |
|
|
$chunk->{text} =~ m/^\s*\`include\s+\"(.*?)\"/ ;
|
2046 |
|
|
# revisit - need to check for recursive includes
|
2047 |
|
|
print " Found at line $chunk->{line} : include $1\n" if $debug;
|
2048 |
|
|
$self->{files}{$file}{includes}{_ffile($1)}=$chunk->{line};
|
2049 |
|
|
my $inc_file = $1;
|
2050 |
|
|
my $inc_file_and_path = _scan_dirs($inc_file,$inc_dirs,$file_dir);
|
2051 |
|
|
if ($inc_file_and_path) {
|
2052 |
|
|
push(@fileStack,$chunkRead,$f);
|
2053 |
|
|
$f = $inc_file_and_path;
|
2054 |
|
|
$file=_ffile($f);
|
2055 |
|
|
$file_dir = dirname($f);
|
2056 |
|
|
|
2057 |
|
|
if (!exists($self->{files}{$file})) {
|
2058 |
|
|
_init_file($self->{files},$f);
|
2059 |
|
|
if (exists($rs->{modules}{$rs->{module}})) {
|
2060 |
|
|
$self->{files}{$file}{contexts}{"1"}{value} =
|
2061 |
|
|
$rs->{modules}{$rs->{module}};
|
2062 |
|
|
}
|
2063 |
|
|
}
|
2064 |
|
|
print "\n Include: $f " unless $quiet;
|
2065 |
|
|
$chunkRead=rvp->chunk_read_init($f,0);
|
2066 |
|
|
}
|
2067 |
|
|
else {
|
2068 |
|
|
$self->_add_warning("$file:$chunk->{line}: Include file $inc_file not found");
|
2069 |
|
|
}
|
2070 |
|
|
next;
|
2071 |
|
|
}
|
2072 |
|
|
|
2073 |
|
|
if (defined($pp_ignore) && $pp_ignore eq "XX") { # no endif
|
2074 |
|
|
$$pp_ignore = $chunk->{line};
|
2075 |
|
|
}
|
2076 |
|
|
}
|
2077 |
|
|
# check if we were included from another file
|
2078 |
|
|
if (0==scalar(@fileStack)) {
|
2079 |
|
|
print "Stack is empty\n" if $debug;
|
2080 |
|
|
last;
|
2081 |
|
|
}
|
2082 |
|
|
else {
|
2083 |
|
|
$f = pop(@fileStack);
|
2084 |
|
|
$chunkRead = pop(@fileStack);
|
2085 |
|
|
$file = _ffile($f);
|
2086 |
|
|
$file_dir = dirname($f);
|
2087 |
|
|
print "\n Back to $f" unless $quiet;
|
2088 |
|
|
}
|
2089 |
|
|
}
|
2090 |
|
|
|
2091 |
|
|
print "\n" unless $quiet;
|
2092 |
|
|
|
2093 |
|
|
$self->_check_end_state($file,$self->{files}{$file}{lines},$ps);
|
2094 |
|
|
|
2095 |
|
|
}
|
2096 |
|
|
|
2097 |
|
|
sub _open_file {
|
2098 |
|
|
my ($f) = @_;
|
2099 |
|
|
local (*F);
|
2100 |
|
|
|
2101 |
|
|
print "Searching $f " unless $quiet;
|
2102 |
|
|
open(F,"<$f") || die "Error: can not open file $f to read: $!\n ";
|
2103 |
|
|
return *F;
|
2104 |
|
|
}
|
2105 |
|
|
|
2106 |
|
|
# only for use while parsing - returns the last defined value
|
2107 |
|
|
# in a multiple define case, and also sets up the {used} info
|
2108 |
|
|
# for use later when querying the database
|
2109 |
|
|
# returns ($value,$errcode)
|
2110 |
|
|
# where $errcode = 0 value ok
|
2111 |
|
|
# 1 value never defined
|
2112 |
|
|
# 2 value has been undefined
|
2113 |
|
|
sub _parsing_get_define_value {
|
2114 |
|
|
my ($defines,$define,$file,$line) = @_;
|
2115 |
|
|
|
2116 |
|
|
if (!exists( $defines->{$define} )) {
|
2117 |
|
|
return ('',1);
|
2118 |
|
|
}
|
2119 |
|
|
my $index = 0;
|
2120 |
|
|
my $dh = $defines->{$define};
|
2121 |
|
|
|
2122 |
|
|
if ( 1 < @{$dh->{defined}} ) {
|
2123 |
|
|
$index = $#{$dh->{defined}};
|
2124 |
|
|
|
2125 |
|
|
$dh->{used}{$file}{$line} = $index;
|
2126 |
|
|
}
|
2127 |
|
|
|
2128 |
|
|
if ($dh->{defined}[$index]{undefed}) {
|
2129 |
|
|
$dh->{used}{$file}{$line} = "XX";
|
2130 |
|
|
return ('',2);
|
2131 |
|
|
}
|
2132 |
|
|
|
2133 |
|
|
return ( $dh->{defined}[$index]{value} , 0 );
|
2134 |
|
|
}
|
2135 |
|
|
|
2136 |
|
|
sub _parsing_is_defined {
|
2137 |
|
|
my ($defines,$define,$file,$line) = @_;
|
2138 |
|
|
|
2139 |
|
|
my $v;
|
2140 |
|
|
my $errcode;
|
2141 |
|
|
($v,$errcode) = _parsing_get_define_value($defines,$define,$file,$line);
|
2142 |
|
|
if ( ($errcode == 1) || # never defined
|
2143 |
|
|
($errcode == 2) ) { # defined then undefed
|
2144 |
|
|
return 0;
|
2145 |
|
|
}
|
2146 |
|
|
elsif ($errcode == 0) {
|
2147 |
|
|
return 1;
|
2148 |
|
|
}
|
2149 |
|
|
else {
|
2150 |
|
|
die "parsing_is_defined internal error code=$errcode";
|
2151 |
|
|
}
|
2152 |
|
|
}
|
2153 |
|
|
|
2154 |
|
|
sub _undef_define {
|
2155 |
|
|
my ($defines,$define) = @_;
|
2156 |
|
|
|
2157 |
|
|
if (exists( $defines->{$define} )) {
|
2158 |
|
|
my $index = $#{$defines->{$define}{defined}};
|
2159 |
|
|
$defines->{$define}{defined}[$index]{undefed} = 1;
|
2160 |
|
|
}
|
2161 |
|
|
}
|
2162 |
|
|
|
2163 |
|
|
###############################################################################
|
2164 |
|
|
# for best use this should be called line by line, so that the
|
2165 |
|
|
# defines get the correct values when defines are defined multiple
|
2166 |
|
|
# times
|
2167 |
|
|
# - this function is only used during the initial parsing of the files
|
2168 |
|
|
# (it has the error reproting code in it), use expand_defines() other times
|
2169 |
|
|
# it also expands on define lines (used to register the use of multiple
|
2170 |
|
|
# define defines) which expand_defines doesn't
|
2171 |
|
|
#
|
2172 |
|
|
sub _parsing_expand_defines {
|
2173 |
|
|
my ($self,$bufp,$file,$line) = @_;
|
2174 |
|
|
|
2175 |
|
|
my $defines = $self->{defines};
|
2176 |
|
|
while ( $$bufp =~ m/^(.*?)\`($VID)/ ) {
|
2177 |
|
|
my $b = $1;
|
2178 |
|
|
my $d = $2;
|
2179 |
|
|
my $dq = quotemeta($d);
|
2180 |
|
|
my $v;
|
2181 |
|
|
my $errCode=0;
|
2182 |
|
|
($v,$errCode)=_parsing_get_define_value($defines,$d,$file,$line);
|
2183 |
|
|
|
2184 |
|
|
if ($errCode == 0) { # no error
|
2185 |
|
|
$$bufp =~ s/\`$dq/$v/;
|
2186 |
|
|
}
|
2187 |
|
|
else {
|
2188 |
|
|
if ($errCode == 2) { # defined but then undefed
|
2189 |
|
|
$self->_add_warning("$file:$line: define `$d used after undef");
|
2190 |
|
|
$$bufp =~ s/\`$dq//;
|
2191 |
|
|
}
|
2192 |
|
|
elsif ($b =~ m/^\s*$/) {
|
2193 |
|
|
$self->_add_warning("$file:$line: unknown define: `$d, guessing it is a compiler directive");
|
2194 |
|
|
$$bufp='';
|
2195 |
|
|
}
|
2196 |
|
|
else {
|
2197 |
|
|
$self->_add_warning("$file:$line: found undefined define `$d");
|
2198 |
|
|
$$bufp =~ s/\`$dq//;
|
2199 |
|
|
}
|
2200 |
|
|
}
|
2201 |
|
|
}
|
2202 |
|
|
}
|
2203 |
|
|
|
2204 |
|
|
###############################################################################
|
2205 |
|
|
# Look through all the include/library directories for an include/library file
|
2206 |
|
|
# optional $file_dir is used when including - here a relative path is
|
2207 |
|
|
# relative to the file doing the including, so check this it checks this
|
2208 |
|
|
sub _scan_dirs {
|
2209 |
|
|
my ($fname,$inc_dirs,$file_dir) = @_;
|
2210 |
|
|
my ($dir);
|
2211 |
|
|
|
2212 |
|
|
if ( $fname =~ m|^/| ) { # an absolute path
|
2213 |
|
|
return "$fname" if ( -r "$fname" && ! -d "$fname");
|
2214 |
|
|
}
|
2215 |
|
|
if (defined($file_dir) && -r "$file_dir/$fname" && ! -d "$file_dir/$fname") {
|
2216 |
|
|
return "$file_dir/$fname";
|
2217 |
|
|
}
|
2218 |
|
|
else {
|
2219 |
|
|
foreach $dir (@{$inc_dirs}) {
|
2220 |
|
|
$dir =~ s|/$||;
|
2221 |
|
|
return "$dir/$fname" if ( -r "$dir/$fname" && ! -d "$dir/$fname");
|
2222 |
|
|
}
|
2223 |
|
|
}
|
2224 |
|
|
return '';
|
2225 |
|
|
}
|
2226 |
|
|
|
2227 |
|
|
###############################################################################
|
2228 |
|
|
# Take a look through the unresolved modules , delete any that have already
|
2229 |
|
|
# been found, and for the others look on the search path
|
2230 |
|
|
#
|
2231 |
|
|
sub _resolve_modules {
|
2232 |
|
|
my ($self,$lib_dirs, $lib_exts)= @_;
|
2233 |
|
|
my ($m,$file,@resolved,$lib_ext);
|
2234 |
|
|
|
2235 |
|
|
@resolved=();
|
2236 |
|
|
foreach $m (sort (keys %{$self->{unresolved_modules}})) {
|
2237 |
|
|
if ( exists( $self->{modules}{$m} )) {
|
2238 |
|
|
delete( $self->{unresolved_modules}{$m} );
|
2239 |
|
|
}
|
2240 |
|
|
else {
|
2241 |
|
|
foreach $lib_ext (@{$lib_exts}) {
|
2242 |
|
|
if ($file = _scan_dirs("$m$lib_ext",$lib_dirs)){
|
2243 |
|
|
delete( $self->{unresolved_modules}{$m} );
|
2244 |
|
|
print "resolve_modules: found $m in $file\n" if $debug;
|
2245 |
|
|
push(@resolved,$file);
|
2246 |
|
|
last;
|
2247 |
|
|
}
|
2248 |
|
|
}
|
2249 |
|
|
}
|
2250 |
|
|
}
|
2251 |
|
|
return @resolved;
|
2252 |
|
|
}
|
2253 |
|
|
|
2254 |
|
|
|
2255 |
|
|
###############################################################################
|
2256 |
|
|
# Initialize fdata->{files}{FILE} which stores file data
|
2257 |
|
|
#
|
2258 |
|
|
sub _init_file {
|
2259 |
|
|
my ($fdataf,$file) = @_;
|
2260 |
|
|
my ($fb);
|
2261 |
|
|
$fb = _ffile($file);
|
2262 |
|
|
$fdataf->{$fb} = {}; # set up hash for each file
|
2263 |
|
|
$fdataf->{$fb}{full_name} = $file;
|
2264 |
|
|
$fdataf->{$fb}{anchors} = {};
|
2265 |
|
|
$fdataf->{$fb}{modules} = {};
|
2266 |
|
|
$fdataf->{$fb}{contexts} = {};
|
2267 |
|
|
$fdataf->{$fb}{includes} = {};
|
2268 |
|
|
$fdataf->{$fb}{inc_done} = 0;
|
2269 |
|
|
$fdataf->{$fb}{lines} = 0;
|
2270 |
|
|
$fdataf->{$fb}{instance_lines} = {};
|
2271 |
|
|
$fdataf->{$fb}{define_lines} = {};
|
2272 |
|
|
$fdataf->{$fb}{included_by} = [];
|
2273 |
|
|
|
2274 |
|
|
}
|
2275 |
|
|
|
2276 |
|
|
###############################################################################
|
2277 |
|
|
# Initialize fdata->{FILE}{modules}{MODULE} which stores
|
2278 |
|
|
# module (or macromodule or primitive) data
|
2279 |
|
|
#
|
2280 |
|
|
sub _init_module {
|
2281 |
|
|
my ($modules,$module,$file,$line,$type) = @_;
|
2282 |
|
|
|
2283 |
|
|
|
2284 |
|
|
die "Error: attempt to reinit module" if (exists($modules->{$module}));
|
2285 |
|
|
|
2286 |
|
|
$modules->{$module}{line} = $line;
|
2287 |
|
|
$modules->{$module}{name} = $module;
|
2288 |
|
|
$modules->{$module}{type} = $type;
|
2289 |
|
|
$modules->{$module}{end} = -1;
|
2290 |
|
|
$modules->{$module}{file} = $file;
|
2291 |
|
|
$modules->{$module}{t_and_f} = {}; # tasks and functions
|
2292 |
|
|
$modules->{$module}{signals} = {};
|
2293 |
|
|
$modules->{$module}{parameter_order}= [];
|
2294 |
|
|
$modules->{$module}{parameters}= {};
|
2295 |
|
|
$modules->{$module}{instances} = []; # things that this module instantiates
|
2296 |
|
|
$modules->{$module}{inst_by} = []; # things that instantiated this module
|
2297 |
|
|
$modules->{$module}{port_order} = [];
|
2298 |
|
|
$modules->{$module}{named_ports} = 1; # assume named ports in instantiations
|
2299 |
|
|
$modules->{$module}{duplicate} = 0; # set if another definition is found
|
2300 |
|
|
|
2301 |
|
|
}
|
2302 |
|
|
|
2303 |
|
|
###############################################################################
|
2304 |
|
|
# Initialize fdata->{FILE}{modules}{MODULE}{t_and_f}{TF} which
|
2305 |
|
|
# stores tasks and functions' data
|
2306 |
|
|
#
|
2307 |
|
|
sub _init_t_and_f {
|
2308 |
|
|
my ($self,$module,$type,$tf,$file,$line,$anchor) = @_;
|
2309 |
|
|
|
2310 |
|
|
if (exists($module->{t_and_f}{$tf})) {
|
2311 |
|
|
$self->_add_warning("$file:$line new definition of $tf ".
|
2312 |
|
|
"(discarding previous from ".
|
2313 |
|
|
"$module->{t_and_f}{$tf}{file}:$module->{t_and_f}{$tf}{line})");
|
2314 |
|
|
}
|
2315 |
|
|
$module->{t_and_f}{$tf} = {};
|
2316 |
|
|
$module->{t_and_f}{$tf}{type} = $type;
|
2317 |
|
|
$module->{t_and_f}{$tf}{name} = $tf;
|
2318 |
|
|
$module->{t_and_f}{$tf}{line} = $line;
|
2319 |
|
|
$module->{t_and_f}{$tf}{end} = -1;
|
2320 |
|
|
$module->{t_and_f}{$tf}{file} = $file;
|
2321 |
|
|
$module->{t_and_f}{$tf}{signals} = {};
|
2322 |
|
|
$module->{t_and_f}{$tf}{anchor} = $anchor;
|
2323 |
|
|
# point up at things to share with module:
|
2324 |
|
|
# - task and functions
|
2325 |
|
|
# - module signals
|
2326 |
|
|
$module->{t_and_f}{$tf}{t_and_f} = $module->{t_and_f};
|
2327 |
|
|
$module->{t_and_f}{$tf}{parameters} = $module->{parameters};
|
2328 |
|
|
$module->{t_and_f}{$tf}{parameter_order} = $module->{parameter_order};
|
2329 |
|
|
$module->{t_and_f}{$tf}{m_signals} = $module->{signals};
|
2330 |
|
|
}
|
2331 |
|
|
|
2332 |
|
|
# note returns 1 if a signal is added (and an anchor needs to be dropped)
|
2333 |
|
|
sub _init_signal {
|
2334 |
|
|
my ($self,$signals,$name,$type,$type2,$range,$file,$line,$warnDuplicate,$dims) = @_;
|
2335 |
|
|
|
2336 |
|
|
if (exists( $signals->{$name} )) {
|
2337 |
|
|
if ($warnDuplicate) {
|
2338 |
|
|
if (($signals->{$name}{type} eq "output")||
|
2339 |
|
|
($signals->{$name}{type} eq "inout")||
|
2340 |
|
|
($signals->{$name}{type} eq "input")) {
|
2341 |
|
|
if (($signals->{$name}{type} eq "input")
|
2342 |
|
|
&& ($type eq "reg")) {
|
2343 |
|
|
$self->_add_warning("$file:$line: ignoring definition".
|
2344 |
|
|
" of input $name as reg (defined as input at".
|
2345 |
|
|
" $signals->{$name}{file}:$signals->{$name}{line})");
|
2346 |
|
|
}
|
2347 |
|
|
else {
|
2348 |
|
|
$signals->{$name}{type2}=$type;
|
2349 |
|
|
}
|
2350 |
|
|
}
|
2351 |
|
|
elsif (($signals->{$name}{type} eq "reg")&& # reg before output
|
2352 |
|
|
(($type eq "output") ||
|
2353 |
|
|
($type eq "inout"))) {
|
2354 |
|
|
$signals->{$name}{type}=$type;
|
2355 |
|
|
$signals->{$name}{type2}="reg";
|
2356 |
|
|
}
|
2357 |
|
|
else {
|
2358 |
|
|
$self->_add_warning("$file:$line: ignoring another definition".
|
2359 |
|
|
" of signal $name ($type) first seen as".
|
2360 |
|
|
" $signals->{$name}{type} at".
|
2361 |
|
|
" $signals->{$name}{file}:$signals->{$name}{line}");
|
2362 |
|
|
}
|
2363 |
|
|
}
|
2364 |
|
|
return 0;
|
2365 |
|
|
}
|
2366 |
|
|
else {
|
2367 |
|
|
$signals->{$name} = { type => $type,
|
2368 |
|
|
file => $file,
|
2369 |
|
|
line => $line,
|
2370 |
|
|
a_line => -1,
|
2371 |
|
|
a_file => "",
|
2372 |
|
|
i_line => -1,
|
2373 |
|
|
i_file => "",
|
2374 |
|
|
port_con => [],
|
2375 |
|
|
con_to => [],
|
2376 |
|
|
posedge => 0,
|
2377 |
|
|
negedge => 0,
|
2378 |
|
|
type2 => $type2,
|
2379 |
|
|
source => { checked => 0, file => "" ,
|
2380 |
|
|
line => "" },
|
2381 |
|
|
range => $range,
|
2382 |
|
|
dimensions => $dims,
|
2383 |
|
|
};
|
2384 |
|
|
return 1;
|
2385 |
|
|
}
|
2386 |
|
|
}
|
2387 |
|
|
|
2388 |
|
|
###############################################################################
|
2389 |
|
|
# Add an anchor to the list of anchors that need to be put in
|
2390 |
|
|
# the file
|
2391 |
|
|
#
|
2392 |
|
|
sub _add_anchor {
|
2393 |
|
|
my ($anchors,$line,$name) = @_;
|
2394 |
|
|
|
2395 |
|
|
my ($a,$no_name_exists);
|
2396 |
|
|
|
2397 |
|
|
if (! exists($anchors->{$line}) ) {
|
2398 |
|
|
$anchors->{$line} = [];
|
2399 |
|
|
}
|
2400 |
|
|
|
2401 |
|
|
if ( $name ) {
|
2402 |
|
|
push( @{$anchors->{$line}} , $name );
|
2403 |
|
|
}
|
2404 |
|
|
else {
|
2405 |
|
|
# if no name is specified then you'll get the line number
|
2406 |
|
|
# as the name, but make sure this only happens once
|
2407 |
|
|
$no_name_exists = 0;
|
2408 |
|
|
foreach $a ( @{$anchors->{$line}} ) {
|
2409 |
|
|
if ($a eq $line) {
|
2410 |
|
|
$no_name_exists=1;
|
2411 |
|
|
last;
|
2412 |
|
|
}
|
2413 |
|
|
}
|
2414 |
|
|
push( @{$anchors->{$line}} , $line ) unless ($no_name_exists);
|
2415 |
|
|
}
|
2416 |
|
|
}
|
2417 |
|
|
|
2418 |
|
|
sub _add_define {
|
2419 |
|
|
my ($defines,$def_name,$def_value,$file,$line) = @_;
|
2420 |
|
|
|
2421 |
|
|
$def_value = '' if (!defined($def_value));
|
2422 |
|
|
$def_value =~ s/\s+$//; # remove whitespace from end of define
|
2423 |
|
|
|
2424 |
|
|
if (!exists($defines->{$def_name})) {
|
2425 |
|
|
$defines->{$def_name} = { defined => [] , used => {} };
|
2426 |
|
|
}
|
2427 |
|
|
|
2428 |
|
|
if ( (1 == @{$defines->{$def_name}{defined}}) &&
|
2429 |
|
|
($defines->{$def_name}{defined}[0]{file} eq $file) &&
|
2430 |
|
|
($defines->{$def_name}{defined}[0]{line} == $line) ) {
|
2431 |
|
|
# if the define is already defined once (and only once) and that
|
2432 |
|
|
# was the same def (file & line the same - for instance in included
|
2433 |
|
|
# file) then there is no need to do anything
|
2434 |
|
|
}
|
2435 |
|
|
else {
|
2436 |
|
|
push (@{$defines->{$def_name}{defined}},
|
2437 |
|
|
{ line => $line, file => $file ,
|
2438 |
|
|
value => $def_value, undefed => 0 });
|
2439 |
|
|
}
|
2440 |
|
|
}
|
2441 |
|
|
|
2442 |
|
|
###############################################################################
|
2443 |
|
|
# Cross referencing
|
2444 |
|
|
###############################################################################
|
2445 |
|
|
|
2446 |
|
|
###############################################################################
|
2447 |
|
|
# Cross-reference all the files:
|
2448 |
|
|
# - find the modules and set up $self->{modules}
|
2449 |
|
|
# - store the data about where it is instatiated with each module
|
2450 |
|
|
# - check for self instantiation
|
2451 |
|
|
# - check for files with modules + instances outside modules
|
2452 |
|
|
# - set a_line for signals driven by output and i_line
|
2453 |
|
|
#
|
2454 |
|
|
sub _cross_reference {
|
2455 |
|
|
my ($self) = @_;
|
2456 |
|
|
my ($f,$m,$fr,$mr,$m2,$inst,$sig,$sigp,$port_con,$param,$i,$port,$con_to);
|
2457 |
|
|
|
2458 |
|
|
# stores the instantiation data in an
|
2459 |
|
|
# array so that we can easily tell which modules
|
2460 |
|
|
# are disconnected and which are the tops of the
|
2461 |
|
|
# hierarchy and makes it easier to go up
|
2462 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2463 |
|
|
print " Making inst_by for $m\n" if $debug;
|
2464 |
|
|
foreach $m2 (sort (keys %{$self->{modules}})) {
|
2465 |
|
|
foreach $inst (@{$self->{modules}{$m2}{instances}}) {
|
2466 |
|
|
if (($inst->{module} eq $m) &&
|
2467 |
|
|
exists($self->{modules}{$m})) {
|
2468 |
|
|
print " inst by $m2\n" if $debug;
|
2469 |
|
|
push( @{$self->{modules}{$m}{inst_by}},
|
2470 |
|
|
{ module => $m2,
|
2471 |
|
|
file => $inst->{file},
|
2472 |
|
|
inst => $inst->{inst_name} ,
|
2473 |
|
|
line => $inst->{line} } );
|
2474 |
|
|
}
|
2475 |
|
|
}
|
2476 |
|
|
}
|
2477 |
|
|
}
|
2478 |
|
|
|
2479 |
|
|
# Find any modules that appear to instantiate themselves
|
2480 |
|
|
# (to prevent getting into infinite recursions later on)
|
2481 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2482 |
|
|
print " Checking self instantiations for $m\n" if $debug;
|
2483 |
|
|
foreach $inst (@{$self->{modules}{$m}{instances}}) {
|
2484 |
|
|
if ($inst->{module} eq $m) {
|
2485 |
|
|
$self->_add_warning("$inst->{file}:$inst->{line}: $m ".
|
2486 |
|
|
"instantiates itself");
|
2487 |
|
|
$inst->{module} = '_ERROR_SELF_INSTANTIATION_';
|
2488 |
|
|
# remove the port con for all signals not attached
|
2489 |
|
|
foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) {
|
2490 |
|
|
$sigp = $self->{modules}{$m}{signals}{$sig};
|
2491 |
|
|
my $port_con_ok=[];
|
2492 |
|
|
foreach $port_con (@{$sigp->{port_con}}) {
|
2493 |
|
|
if ($port_con->{module} ne $m) { push(@$port_con_ok,$port_con); }
|
2494 |
|
|
else { print " Deleting connection for $sig\n" if $debug; }
|
2495 |
|
|
}
|
2496 |
|
|
$sigp->{port_con} = $port_con_ok;
|
2497 |
|
|
}
|
2498 |
|
|
}
|
2499 |
|
|
}
|
2500 |
|
|
}
|
2501 |
|
|
|
2502 |
|
|
# Go through instances without named ports (port will be a number instead) and
|
2503 |
|
|
# resolve name if you can, otherwise delete. These can appear in signal's port_con
|
2504 |
|
|
# lists and in instances connections lists.
|
2505 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2506 |
|
|
if (0 == $self->{modules}{$m}{named_ports}) {
|
2507 |
|
|
$f = $self->{modules}{$m}{file}; # for error messages
|
2508 |
|
|
print " Resolving numbered port connections in $m\n" if $debug;
|
2509 |
|
|
foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) {
|
2510 |
|
|
print " doing $sig\n" if $debug;
|
2511 |
|
|
$sigp = $self->{modules}{$m}{signals}{$sig};
|
2512 |
|
|
|
2513 |
|
|
foreach $port_con (@{$sigp->{port_con}}) {
|
2514 |
|
|
if ($port_con->{port} =~ m/^[0-9]/ ) {
|
2515 |
|
|
if ( exists( $self->{modules}{$port_con->{module}}) ) {
|
2516 |
|
|
$m2 = $self->{modules}{$port_con->{module}};
|
2517 |
|
|
if (defined($m2->{port_order}[$port_con->{port}])) {
|
2518 |
|
|
$port_con->{port}=$m2->{port_order}[$port_con->{port}];
|
2519 |
|
|
}
|
2520 |
|
|
else {
|
2521 |
|
|
$self->_add_warning("$port_con->{file}:$port_con->{line}:".
|
2522 |
|
|
" could not resolve port number to name");
|
2523 |
|
|
}
|
2524 |
|
|
}
|
2525 |
|
|
}
|
2526 |
|
|
}
|
2527 |
|
|
}
|
2528 |
|
|
|
2529 |
|
|
foreach $inst (@{$self->{modules}{$m}{instances}}) {
|
2530 |
|
|
if ( exists( $self->{modules}{$inst->{module}}) ) {
|
2531 |
|
|
$m2 = $self->{modules}{$inst->{module}};
|
2532 |
|
|
foreach $port (sort (keys %{$inst->{connections}})) {
|
2533 |
|
|
last if ($port !~ m/^[0-9]/); # if any are named, all are named
|
2534 |
|
|
if (defined($m2->{port_order}[$port])) {
|
2535 |
|
|
# move old connection to named port
|
2536 |
|
|
$inst->{connections}{$m2->{port_order}[$port]} =
|
2537 |
|
|
$inst->{connections}{$port};
|
2538 |
|
|
# remove old numbered port from hash
|
2539 |
|
|
delete($inst->{connections}{$port});
|
2540 |
|
|
}
|
2541 |
|
|
else {
|
2542 |
|
|
$self->_add_warning("$inst->{file}:$inst->{line}:".
|
2543 |
|
|
"could not resolve port number $port to name)");
|
2544 |
|
|
}
|
2545 |
|
|
}
|
2546 |
|
|
}
|
2547 |
|
|
}
|
2548 |
|
|
}
|
2549 |
|
|
}
|
2550 |
|
|
|
2551 |
|
|
# Go through all instances with parameter lists and try to resolve names parameter
|
2552 |
|
|
#
|
2553 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2554 |
|
|
foreach $inst (@{$self->{modules}{$m}{instances}}) {
|
2555 |
|
|
if ($inst->{parameters}) {
|
2556 |
|
|
if ( exists( $self->{modules}{$inst->{module}}) ) {
|
2557 |
|
|
my $mp=$self->{modules}{$inst->{module}};
|
2558 |
|
|
foreach my $p (sort (keys %{$inst->{parameters}})){
|
2559 |
|
|
last if ( $p !~ m/^[0-9]+$/ );
|
2560 |
|
|
my $pn = $mp->{parameter_order}[$p];
|
2561 |
|
|
if ($pn) {
|
2562 |
|
|
$inst->{parameters}{$pn} =
|
2563 |
|
|
$inst->{parameters}{$p};
|
2564 |
|
|
delete($inst->{parameters}{$p});
|
2565 |
|
|
print "$inst->{parameters}{$pn}{file}:".
|
2566 |
|
|
"$inst->{parameters}{$pn}{line}: ".
|
2567 |
|
|
"Resolved $p to $pn = $inst->{parameters}{$pn}{value}\n"
|
2568 |
|
|
if $debug;
|
2569 |
|
|
}
|
2570 |
|
|
else {
|
2571 |
|
|
$self->_add_warning("$inst->{parameters}{$p}{file}:".
|
2572 |
|
|
"$inst->{parameters}{$p}{line} ".
|
2573 |
|
|
"could not resolve parameter number $p to name");
|
2574 |
|
|
}
|
2575 |
|
|
}
|
2576 |
|
|
}
|
2577 |
|
|
}
|
2578 |
|
|
}
|
2579 |
|
|
}
|
2580 |
|
|
|
2581 |
|
|
# Go through all the modules and each signal inside
|
2582 |
|
|
# looking at whether the signal is connected to any outputs
|
2583 |
|
|
# (set the a_line on the first one if it is not already set)
|
2584 |
|
|
# Also, when you see a signal connected to an input (and that
|
2585 |
|
|
# submod is only instantiated once) reach down into the submod
|
2586 |
|
|
# and set the i_line of that signal, so that clicking on the
|
2587 |
|
|
# input can pop you up to the line that input is driven in
|
2588 |
|
|
# one of the instantiations
|
2589 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2590 |
|
|
print " Finding port connections in $m\n" if $debug;
|
2591 |
|
|
foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) {
|
2592 |
|
|
print " checking signal $sig\n" if $debug;
|
2593 |
|
|
$sigp = $self->{modules}{$m}{signals}{$sig};
|
2594 |
|
|
|
2595 |
|
|
foreach $port_con (@{$sigp->{port_con}}) {
|
2596 |
|
|
if ( exists( $self->{modules}{$port_con->{module}}) ) {
|
2597 |
|
|
print " connection to $port_con->{module}\n" if $debug;
|
2598 |
|
|
$m2 = $self->{modules}{$port_con->{module}};
|
2599 |
|
|
if (exists( $m2->{signals}{$port_con->{port}})) {
|
2600 |
|
|
push(@{$m2->{signals}{$port_con->{port}}{con_to}},
|
2601 |
|
|
{ signal => $sig , module => $m , inst => $port_con->{inst}});
|
2602 |
|
|
if ( ($m2->{signals}{$port_con->{port}}{type} eq
|
2603 |
|
|
'output') &&
|
2604 |
|
|
($sigp->{a_line} == -1)) {
|
2605 |
|
|
$sigp->{driven_by_port}=1;
|
2606 |
|
|
$sigp->{a_line} = $port_con->{line};
|
2607 |
|
|
$sigp->{a_file} = $port_con->{file};
|
2608 |
|
|
_add_anchor($self->{files}{$port_con->{file}}{anchors},
|
2609 |
|
|
$port_con->{line},'');
|
2610 |
|
|
}
|
2611 |
|
|
elsif ($m2->{signals}{$port_con->{port}}{type} eq
|
2612 |
|
|
'input') {
|
2613 |
|
|
$m2->{signals}{$port_con->{port}}{driven_by_port}=1;
|
2614 |
|
|
if (scalar(@{$m2->{inst_by}}) &&
|
2615 |
|
|
($m2->{signals}{$port_con->{port}}{i_line}==-1)) {
|
2616 |
|
|
$m2->{signals}{$port_con->{port}}{i_line}=
|
2617 |
|
|
$port_con->{line};
|
2618 |
|
|
$m2->{signals}{$port_con->{port}}{i_file}=
|
2619 |
|
|
$port_con->{file};
|
2620 |
|
|
_add_anchor($self->{files}{$port_con->{file}}{anchors},
|
2621 |
|
|
$port_con->{line},'');
|
2622 |
|
|
print " set i_line $port_con->{port} ".
|
2623 |
|
|
"$port_con->{file}:$port_con->{line}\n" if $debug;
|
2624 |
|
|
}
|
2625 |
|
|
}
|
2626 |
|
|
}
|
2627 |
|
|
}
|
2628 |
|
|
}
|
2629 |
|
|
}
|
2630 |
|
|
}
|
2631 |
|
|
|
2632 |
|
|
# find all signal sources
|
2633 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2634 |
|
|
print " Finding signal sources in $m\n" if $debug;
|
2635 |
|
|
foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) {
|
2636 |
|
|
$sigp = $self->{modules}{$m}{signals}{$sig};
|
2637 |
|
|
next if $sigp->{source}{checked};
|
2638 |
|
|
print " finding signal source for $sig of $m\n" if $debug;
|
2639 |
|
|
$sigp->{source} = $self->_find_signal_source($sigp);
|
2640 |
|
|
}
|
2641 |
|
|
}
|
2642 |
|
|
|
2643 |
|
|
# propagate the posedge, negedge stuff up the hierarchy
|
2644 |
|
|
foreach $m (sort (keys %{$self->{modules}})) {
|
2645 |
|
|
# only do the recursion for top level modules
|
2646 |
|
|
if ( 0== @{$self->{modules}{$m}{inst_by}} ) {
|
2647 |
|
|
$self->_prop_edges($m);
|
2648 |
|
|
}
|
2649 |
|
|
}
|
2650 |
|
|
|
2651 |
|
|
# get included_by information
|
2652 |
|
|
foreach $f ( sort (keys %{$self->{files}} )) {
|
2653 |
|
|
foreach $i ($self->get_files_includes($f)) {
|
2654 |
|
|
if (exists $self->{files}{$i}) {
|
2655 |
|
|
push( @{$self->{files}{$i}{included_by}} , $f );
|
2656 |
|
|
}
|
2657 |
|
|
}
|
2658 |
|
|
}
|
2659 |
|
|
}
|
2660 |
|
|
|
2661 |
|
|
sub _find_signal_source {
|
2662 |
|
|
my ($self,$sigp) = @_;
|
2663 |
|
|
my ($con_to,$port_con,$ret_val);
|
2664 |
|
|
|
2665 |
|
|
if ($sigp->{source}{checked}) {
|
2666 |
|
|
print " source already found\n" if $debug;
|
2667 |
|
|
$ret_val = $sigp->{source};
|
2668 |
|
|
}
|
2669 |
|
|
else {
|
2670 |
|
|
$ret_val = { checked => 1, file => '' , line => '' };
|
2671 |
|
|
if (exists($sigp->{driven_by_port})) {
|
2672 |
|
|
print " drive by port\n" if $debug;
|
2673 |
|
|
foreach $con_to (@{$sigp->{con_to}}) {
|
2674 |
|
|
# if ($self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}}{type} eq 'input') {
|
2675 |
|
|
if ($sigp->{type} eq 'input') {
|
2676 |
|
|
print " following input $con_to->{signal} $con_to->{module} $con_to->{inst}\n" if $debug;
|
2677 |
|
|
if (!exists($self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}}{i_line})) { die "Error: $con_to->{signal} does not exist $!"; }
|
2678 |
|
|
$ret_val = $self->_find_signal_source(
|
2679 |
|
|
$self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}});
|
2680 |
|
|
}
|
2681 |
|
|
}
|
2682 |
|
|
foreach $port_con (@{$sigp->{port_con}}) {
|
2683 |
|
|
if (exists ($self->{modules}{$port_con->{module}})) {
|
2684 |
|
|
if (exists($self->{modules}{$port_con->{module}}{signals}{$port_con->{port}})) {
|
2685 |
|
|
if ($self->{modules}{$port_con->{module}}{signals}{$port_con->{port}}{type} eq 'output') {
|
2686 |
|
|
print " following output $port_con->{port} $port_con->{module} $port_con->{inst}\n" if $debug;
|
2687 |
|
|
$ret_val = $self->_find_signal_source(
|
2688 |
|
|
$self->{modules}{$port_con->{module}}{signals}{$port_con->{port}});
|
2689 |
|
|
}
|
2690 |
|
|
}
|
2691 |
|
|
else {
|
2692 |
|
|
$self->_add_warning("$port_con->{file}:$port_con->{line}:".
|
2693 |
|
|
" Connection to nonexistent port ".
|
2694 |
|
|
" $port_con->{port} of module $port_con->{module}");
|
2695 |
|
|
}
|
2696 |
|
|
}
|
2697 |
|
|
}
|
2698 |
|
|
}
|
2699 |
|
|
else {
|
2700 |
|
|
if ($sigp->{a_line}==-1) {
|
2701 |
|
|
if ($sigp->{type} eq 'input') {
|
2702 |
|
|
print " signal is an input not driven at higher level\n" if $debug;
|
2703 |
|
|
$ret_val = { checked => 1, file => $sigp->{file} , line => $sigp->{line} };
|
2704 |
|
|
}
|
2705 |
|
|
else {
|
2706 |
|
|
print " signal has unknown source\n" if $debug;
|
2707 |
|
|
}
|
2708 |
|
|
}
|
2709 |
|
|
else {
|
2710 |
|
|
print " signal is driven in this module\n" if $debug;
|
2711 |
|
|
$ret_val = { checked => 1 , file => $sigp->{a_file} , line => $sigp->{a_line} };
|
2712 |
|
|
}
|
2713 |
|
|
}
|
2714 |
|
|
}
|
2715 |
|
|
|
2716 |
|
|
$sigp->{source} = $ret_val;
|
2717 |
|
|
return $ret_val;
|
2718 |
|
|
}
|
2719 |
|
|
|
2720 |
|
|
###############################################################################
|
2721 |
|
|
# Propagate posedge and negedge attributes of signals up the hierarchy
|
2722 |
|
|
#
|
2723 |
|
|
sub _prop_edges {
|
2724 |
|
|
my ($self,$m) = @_;
|
2725 |
|
|
my ($imod,@inst,$sig,$sigp,$port_con,$m2);
|
2726 |
|
|
|
2727 |
|
|
print "Prop_edges $m\n" if $debug;
|
2728 |
|
|
|
2729 |
|
|
for ( ($imod) = $self->get_first_instantiation($m) ;
|
2730 |
|
|
$imod;
|
2731 |
|
|
($imod) = $self->get_next_instantiation()) {
|
2732 |
|
|
push(@inst,$imod) if (exists( $self->{modules}{$imod}));
|
2733 |
|
|
}
|
2734 |
|
|
foreach $imod (@inst) { $self->_prop_edges($imod); }
|
2735 |
|
|
|
2736 |
|
|
# Propagate all the edges up the hierarchy
|
2737 |
|
|
foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) {
|
2738 |
|
|
print " checking signal $sig\n" if $debug;
|
2739 |
|
|
$sigp = $self->{modules}{$m}{signals}{$sig};
|
2740 |
|
|
|
2741 |
|
|
foreach $port_con (@{$sigp->{port_con}}) {
|
2742 |
|
|
if ( exists( $self->{modules}{$port_con->{module}}) ) {
|
2743 |
|
|
print " connection to $port_con->{module}\n" if $debug;
|
2744 |
|
|
$m2 = $self->{modules}{$port_con->{module}};
|
2745 |
|
|
if (exists( $m2->{signals}{$port_con->{port}})) {
|
2746 |
|
|
print "Propagating posedge on $sig from $port_con->{module} to $m\n"
|
2747 |
|
|
if ($debug && (!$sigp->{posedge}) && $m2->{signals}{$port_con->{port}}{posedge});
|
2748 |
|
|
$sigp->{posedge} |= $m2->{signals}{$port_con->{port}}{posedge};
|
2749 |
|
|
$sigp->{negedge} |= $m2->{signals}{$port_con->{port}}{negedge};
|
2750 |
|
|
}
|
2751 |
|
|
}
|
2752 |
|
|
}
|
2753 |
|
|
}
|
2754 |
|
|
}
|
2755 |
|
|
|
2756 |
|
|
|
2757 |
|
|
###############################################################################
|
2758 |
|
|
# given a source file name work out the file without the path
|
2759 |
|
|
#
|
2760 |
|
|
sub _ffile {
|
2761 |
|
|
my ($sfile) = @_;
|
2762 |
|
|
|
2763 |
|
|
$sfile =~ s/^.*[\/\\]//;
|
2764 |
|
|
|
2765 |
|
|
return $sfile;
|
2766 |
|
|
}
|
2767 |
|
|
|
2768 |
|
|
sub _add_warning {
|
2769 |
|
|
my ($self,$p) = @_;
|
2770 |
|
|
|
2771 |
|
|
print "Warning:$p\n" if $debug;
|
2772 |
|
|
push (@{$self->{problems}},"Warning:$p");
|
2773 |
|
|
}
|
2774 |
|
|
sub _add_confused {
|
2775 |
|
|
my ($self,$p) = @_;
|
2776 |
|
|
|
2777 |
|
|
print "Confused:$p\n" if $debug;
|
2778 |
|
|
push (@{$self->{problems}},"Confused:$p");
|
2779 |
|
|
}
|
2780 |
|
|
|
2781 |
|
|
###############################################################################
|
2782 |
|
|
#
|
2783 |
|
|
BEGIN {
|
2784 |
|
|
$baseEval = {
|
2785 |
|
|
START => {
|
2786 |
|
|
MODULE => '$rs->{t}={ type=>$match, line=>$line };',
|
2787 |
|
|
},
|
2788 |
|
|
MODULE => {
|
2789 |
|
|
SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};',
|
2790 |
|
|
# if you add to this also edit {AFTER_INST}{COMMA}
|
2791 |
|
|
INST => '$rs->{t}={ mod=>$match, line=>$line, name=>"" , port=>0 ,
|
2792 |
|
|
params=>{}, param_number=>0 , portName=>"" , vids=>[]};',
|
2793 |
|
|
},
|
2794 |
|
|
MODULE_NAME => {
|
2795 |
|
|
NAME => 'my $nState="MODULE_PPL";
|
2796 |
|
|
my $type = $rs->{t}{type}; $rs->{t}=undef;',
|
2797 |
|
|
},
|
2798 |
|
|
IN_CONCAT => {
|
2799 |
|
|
VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line}) if (exists($rs->{t}{vids}));',
|
2800 |
|
|
},
|
2801 |
|
|
IN_BRACKET => {
|
2802 |
|
|
VID => 'IN_CONCAT:VID',
|
2803 |
|
|
},
|
2804 |
|
|
SCALARED_OR_VECTORED => {
|
2805 |
|
|
TYPE => 'if ($match eq "reg") { $rs->{t}{type2} = "reg"; }'
|
2806 |
|
|
},
|
2807 |
|
|
SIGNAL_NAME => {
|
2808 |
|
|
VID => '$rs->{t}{name}=$match; $rs->{t}{line}=$line;',
|
2809 |
|
|
},
|
2810 |
|
|
SIGNAL_AFTER_EQUALS => {
|
2811 |
|
|
END => '$rs->{t}=undef;',
|
2812 |
|
|
},
|
2813 |
|
|
INST_PARAM_BRACKET => {
|
2814 |
|
|
NO_BRACKET => '$self->_add_warning("$file:$line: possible missing brackets after \# in instantiation");',
|
2815 |
|
|
},
|
2816 |
|
|
INST_NAME => {
|
2817 |
|
|
VID => '$rs->{t}{name}=$match;',
|
2818 |
|
|
},
|
2819 |
|
|
INST_PORTS => {
|
2820 |
|
|
COMMA => '$rs->{t}{port}++;',
|
2821 |
|
|
},
|
2822 |
|
|
INST_PORT_NAME => {
|
2823 |
|
|
NAME => '$rs->{t}{portName}=$match;
|
2824 |
|
|
$rs->{t}{vids} = [];', # throw away any instance parameters picked up
|
2825 |
|
|
},
|
2826 |
|
|
INST_NAMED_PORT_CON => {
|
2827 |
|
|
VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line});',
|
2828 |
|
|
},
|
2829 |
|
|
INST_NAMED_PORT_CON_AFTER => {
|
2830 |
|
|
COMMA => 'if ($rs->{t}{portName} eq "") { $rs->{t}{portName}=$rs->{t}{port}++; }
|
2831 |
|
|
my @vids = @{$rs->{t}{vids}};
|
2832 |
|
|
my $portName = $rs->{t}{portName};
|
2833 |
|
|
$rs->{t}{portName}="";
|
2834 |
|
|
$rs->{t}{vids}=[];',
|
2835 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2836 |
|
|
},
|
2837 |
|
|
INST_NUMBERED_PORT => {
|
2838 |
|
|
COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2839 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2840 |
|
|
VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line});',
|
2841 |
|
|
},
|
2842 |
|
|
AFTER_INST => {
|
2843 |
|
|
SEMICOLON => '$rs->{t}=undef;',
|
2844 |
|
|
COMMA => '$rs->{t}{line}=$line;
|
2845 |
|
|
$rs->{t}{name}="";
|
2846 |
|
|
$rs->{t}{port}=0;
|
2847 |
|
|
$rs->{t}{portName}="";
|
2848 |
|
|
$rs->{t}{vids}=[];',
|
2849 |
|
|
},
|
2850 |
|
|
SIGNAL_AFTER_NAME => {
|
2851 |
|
|
SEMICOLON => '$rs->{t}=undef;',
|
2852 |
|
|
},
|
2853 |
|
|
IN_EVENT_BRACKET => {
|
2854 |
|
|
EDGE => '$rs->{t}={ type=>$match };',
|
2855 |
|
|
},
|
2856 |
|
|
IN_EVENT_BRACKET_EDGE => {
|
2857 |
|
|
VID => 'my $edgeType = $rs->{t}{type}; $rs->{t}=undef;',
|
2858 |
|
|
},
|
2859 |
|
|
STMNT => {
|
2860 |
|
|
ASSIGN_OR_TASK => '$rs->{t}={ vids=>[{name=>$match,line=>$line}]};',
|
2861 |
|
|
HIER_ASSIGN_OR_TASK => '$rs->{t}={ vids=>[]};',
|
2862 |
|
|
CONCAT => '$rs->{t}={ vids=>[]};',
|
2863 |
|
|
},
|
2864 |
|
|
STMNT_ASSIGN_OR_TASK => { # copy of STMNT_ASSIGN
|
2865 |
|
|
EQUALS => 'my @vids = @{$rs->{t}{vids}}; $rs->{t}=undef;',
|
2866 |
|
|
# Revisit: this arc doesn't exist anymore - put this into smnt_semicolon
|
2867 |
|
|
# SEMICOLON => '$rs->{t}=undef;',
|
2868 |
|
|
BRACKET => '$rs->{t}=undef;',
|
2869 |
|
|
},
|
2870 |
|
|
STMNT_ASSIGN => { # copy of STMNT_ASSIGN_OR_TASK
|
2871 |
|
|
EQUALS => 'STMNT_ASSIGN_OR_TASK:EQUALS',
|
2872 |
|
|
},
|
2873 |
|
|
IN_SIG_RANGE => {
|
2874 |
|
|
END => '$rs->{t}{range}=$fromLastPos;',
|
2875 |
|
|
},
|
2876 |
|
|
IN_MEM_RANGE => {
|
2877 |
|
|
END => 'push(@{$rs->{t}{dimensions}},$fromLastPos);',
|
2878 |
|
|
},
|
2879 |
|
|
ANSI_PORTS_TYPE => { # V2001 ansi ports
|
2880 |
|
|
TYPE => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};',
|
2881 |
|
|
},
|
2882 |
|
|
ANSI_PORTS_TYPE2 => { # V2001 ansi ports
|
2883 |
|
|
TYPE => 'if ($match eq "reg") { $rs->{t}{type2} = "reg"; }',
|
2884 |
|
|
},
|
2885 |
|
|
ANSI_PORTS_SIGNAL_NAME => { # V2001 ansi ports
|
2886 |
|
|
VID => '$rs->{t}{name}=$match; $rs->{t}{line}=$line;',
|
2887 |
|
|
},
|
2888 |
|
|
};
|
2889 |
|
|
|
2890 |
|
|
############################################################
|
2891 |
|
|
# debugEval
|
2892 |
|
|
############################################################
|
2893 |
|
|
$debugEval = {
|
2894 |
|
|
ANSI_PORTS_SIGNAL_NAME => {
|
2895 |
|
|
VID => 'print "Found $rs->{t}{type} $rs->{t}{name} $rs->{t}{range} [$line]\n";',
|
2896 |
|
|
},
|
2897 |
|
|
SIGNAL_NAME => {
|
2898 |
|
|
VID => 'print "Found $rs->{t}{type} $rs->{t}{name} $rs->{t}{range} [$line]\n";',
|
2899 |
|
|
},
|
2900 |
|
|
INST_BRACKET => {
|
2901 |
|
|
PORTS => 'print "found instance $rs->{t}{name} of $rs->{t}{mod} [$rs->{t}{line}]\n";',
|
2902 |
|
|
},
|
2903 |
|
|
INST_NAMED_PORT_CON_AFTER => {
|
2904 |
|
|
COMMA => 'my @vidnames;
|
2905 |
|
|
foreach my $vid (@vids) {push @vidnames,$vid->{name};}
|
2906 |
|
|
print " Port $portName connected to ".join(",",@vidnames)."\n";',
|
2907 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2908 |
|
|
},
|
2909 |
|
|
INST_NUMBERED_PORT => {
|
2910 |
|
|
COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2911 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
2912 |
|
|
},
|
2913 |
|
|
};
|
2914 |
|
|
|
2915 |
|
|
|
2916 |
|
|
############################################################
|
2917 |
|
|
# rvpEval
|
2918 |
|
|
############################################################
|
2919 |
|
|
|
2920 |
|
|
$rvpEval = {
|
2921 |
|
|
MODULE => {
|
2922 |
|
|
ENDMODULE => 'if ((($rs->{p}{type} eq "primitive")&&($match ne "endprimitive"))||
|
2923 |
|
|
(($rs->{p}{type} ne "primitive")&&($match eq "endprimitive"))){
|
2924 |
|
|
$self->_add_warning("$file:$line: module of type".
|
2925 |
|
|
" $rs->{p}{type} ended by $match");
|
2926 |
|
|
}
|
2927 |
|
|
$rs->{modules}{$rs->{module}}{end} = $line;
|
2928 |
|
|
$rs->{module} = "";
|
2929 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= { name=>"",type=>"" };
|
2930 |
|
|
$rs->{p}= undef;',
|
2931 |
|
|
PARAM => '$rs->{t} = { ptype => $match };', # parameter of localparam
|
2932 |
|
|
},
|
2933 |
|
|
MODULE_NAME => {
|
2934 |
|
|
NAME => 'if (exists($rs->{modules}{$match})) {
|
2935 |
|
|
$nState = "IGNORE_MODULE";
|
2936 |
|
|
$rs->{modules}{$match}{duplicate} = 1;
|
2937 |
|
|
$self->_add_warning("$file:$line ignoring new definition of ".
|
2938 |
|
|
"module $match, previous was at ".
|
2939 |
|
|
"$rs->{modules}{$match}{file}:$rs->{modules}{$match}{line})");
|
2940 |
|
|
}
|
2941 |
|
|
else {
|
2942 |
|
|
$rs->{module}=$match;
|
2943 |
|
|
_init_module($rs->{modules},$rs->{module},$file,$line,$type);
|
2944 |
|
|
$rs->{files}{$file}{modules}{$rs->{module}} = $rs->{modules}{$rs->{module}};
|
2945 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module});
|
2946 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}};
|
2947 |
|
|
$rs->{files}{$file}{contexts}{$line}{module_start}= $rs->{module};
|
2948 |
|
|
}',
|
2949 |
|
|
},
|
2950 |
|
|
MODULE_PORTS => {
|
2951 |
|
|
VID => 'push(@{$rs->{p}{port_order}},$match);',
|
2952 |
|
|
},
|
2953 |
|
|
FUNCTION => {
|
2954 |
|
|
NAME => '$rs->{function}=$match;
|
2955 |
|
|
$self->_init_t_and_f($rs->{modules}{$rs->{module}},"function",
|
2956 |
|
|
$rs->{function},$file,$line,$rs->{module}."_".$rs->{function});
|
2957 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module}."_".$rs->{function});
|
2958 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}}{t_and_f}{$rs->{function}};',
|
2959 |
|
|
},
|
2960 |
|
|
TASK => {
|
2961 |
|
|
NAME => '$rs->{task}=$match;
|
2962 |
|
|
$self->_init_t_and_f($rs->{modules}{$rs->{module}},"task",
|
2963 |
|
|
$rs->{task},$file,$line,$rs->{module}. "_" .$rs->{task});
|
2964 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module}. "_" . $rs->{task});
|
2965 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}}{t_and_f}{$rs->{task}};',
|
2966 |
|
|
},
|
2967 |
|
|
ENDTASK => {
|
2968 |
|
|
ENDTASK => '$rs->{modules}{$rs->{module}}{t_and_f}{$rs->{task}}{end} = $line;
|
2969 |
|
|
$rs->{task}="";
|
2970 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}};',
|
2971 |
|
|
},
|
2972 |
|
|
T_SIGNAL => {
|
2973 |
|
|
SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"" , block=>0};',
|
2974 |
|
|
ENDTASK => 'ENDTASK:ENDTASK',
|
2975 |
|
|
PARAM => 'MODULE:PARAM', # not realy needed yet because T/F parameters are ignored
|
2976 |
|
|
},
|
2977 |
|
|
ENDFUNCTION => {
|
2978 |
|
|
ENDFUNCTION => '$rs->{modules}{$rs->{module}}{t_and_f}{$rs->{function}}{end} = $line;
|
2979 |
|
|
$rs->{function}="";
|
2980 |
|
|
$rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}};',
|
2981 |
|
|
},
|
2982 |
|
|
F_SIGNAL => {
|
2983 |
|
|
SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};',
|
2984 |
|
|
ENDFUNCTION => 'ENDFUNCTION:ENDFUNCTION',
|
2985 |
|
|
PARAM => 'MODULE:PARAM', # not realy needed yet because T/F parameters are ignored
|
2986 |
|
|
},
|
2987 |
|
|
BLOCK_SIGNAL => {
|
2988 |
|
|
SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"" , block=>1};',
|
2989 |
|
|
},
|
2990 |
|
|
PARAM_NAME => {
|
2991 |
|
|
NAME => 'if ( ($rs->{function} eq "") && ($rs->{task} eq "")) { # ignore parameters in tasks and functions
|
2992 |
|
|
$rs->{t}= { file => $file, line => $line , value => "" ,
|
2993 |
|
|
ptype => $rs->{t}{ptype}}; # ptype is same as the last one
|
2994 |
|
|
push(@{$rs->{p}{parameter_order}}, $match)
|
2995 |
|
|
unless ($rs->{t}{ptype} eq "localparam");
|
2996 |
|
|
$rs->{p}{parameters}{$match}=$rs->{t};
|
2997 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,""); }',
|
2998 |
|
|
},
|
2999 |
|
|
PPL_PARAM => {
|
3000 |
|
|
PARAM => '$rs->{t} = { ptype => "parameter" };', # this can't be a localparam
|
3001 |
|
|
},
|
3002 |
|
|
PPL_NAME => {
|
3003 |
|
|
NAME => 'PARAM_NAME:NAME',
|
3004 |
|
|
},
|
3005 |
|
|
PARAM_AFTER_EQUALS => {
|
3006 |
|
|
COMMA => '$rs->{t}{value} = $fromLastPos;',
|
3007 |
|
|
SEMICOLON => 'PARAM_AFTER_EQUALS:COMMA',
|
3008 |
|
|
},
|
3009 |
|
|
PPL_AFTER_EQUALS => {
|
3010 |
|
|
COMMA => 'PARAM_AFTER_EQUALS:COMMA',
|
3011 |
|
|
END => 'PARAM_AFTER_EQUALS:COMMA',
|
3012 |
|
|
},
|
3013 |
|
|
ASSIGN => {
|
3014 |
|
|
VID => 'if ( exists($rs->{p}{signals}{$match}) &&
|
3015 |
|
|
($rs->{p}{signals}{$match}{a_line} == -1)) {
|
3016 |
|
|
$rs->{p}{signals}{$match}{a_line} = $line;
|
3017 |
|
|
$rs->{p}{signals}{$match}{a_file} = $file;
|
3018 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,"");
|
3019 |
|
|
}',
|
3020 |
|
|
},
|
3021 |
|
|
SIGNAL_NAME => { # note skip signals local to a block ({block}==1)
|
3022 |
|
|
VID => 'if ($rs->{t}{block} != 1) {
|
3023 |
|
|
$self->_init_signal($rs->{p}{signals},$match,$rs->{t}{type},$rs->{t}{type2},
|
3024 |
|
|
$rs->{t}{range},$file,$line,1,$rs->{t}{dimensions})
|
3025 |
|
|
&& _add_anchor($rs->{files}{$file}{anchors},$line,"");
|
3026 |
|
|
}',
|
3027 |
|
|
},
|
3028 |
|
|
SIGNAL_AFTER_NAME => { # don't assign a_line for reg at definition, as this is
|
3029 |
|
|
# only the initial value
|
3030 |
|
|
ASSIGN => 'if ($rs->{t}{block} != 1) {
|
3031 |
|
|
if ( $rs->{p}{signals}{$rs->{t}{name}}{type} ne "reg" ) {
|
3032 |
|
|
$rs->{p}{signals}{$rs->{t}{name}}{a_line}=$rs->{t}{line};
|
3033 |
|
|
$rs->{p}{signals}{$rs->{t}{name}}{a_file}=$file;
|
3034 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$rs->{t}{line},"");
|
3035 |
|
|
}
|
3036 |
|
|
}',
|
3037 |
|
|
},
|
3038 |
|
|
INST_PARAM_VALUE => {
|
3039 |
|
|
# Note: the code is nearly the same in INST_PARAM_VALUE:COMMA,
|
3040 |
|
|
# and INST_PARAM_BRACKET:NO_BRACKET, but the first uses $fromLastPos
|
3041 |
|
|
# and the second uses $match to capture the parameter value
|
3042 |
|
|
COMMA => 'my $inst_num= $#{$rs->{p}{instances}};
|
3043 |
|
|
$rs->{t}{params}{$rs->{t}{param_number}} =
|
3044 |
|
|
{ file => $file , line => $line , value => $fromLastPos };
|
3045 |
|
|
$rs->{t}{param_number}++;',
|
3046 |
|
|
END => 'INST_PARAM_VALUE:COMMA',
|
3047 |
|
|
},
|
3048 |
|
|
INST_PARAM_BRACKET => {
|
3049 |
|
|
# Note: the code is nearly the same in INST_PARAM_VALUE:COMMA,
|
3050 |
|
|
# and INST_PARAM_BRACKET:NO_BRACKET, but the first uses $fromLastPos
|
3051 |
|
|
# and the second uses $match to capture the parameter value
|
3052 |
|
|
NO_BRACKET => 'my $inst_num= $#{$rs->{p}{instances}};
|
3053 |
|
|
$rs->{t}{params}{$rs->{t}{param_number}} =
|
3054 |
|
|
{ file => $file , line => $line , value => $match };
|
3055 |
|
|
$rs->{t}{param_number}++;',
|
3056 |
|
|
},
|
3057 |
|
|
INST_BRACKET => {
|
3058 |
|
|
PORTS => '$rs->{unres_mod}{$rs->{t}{mod}}=$rs->{t}{mod};
|
3059 |
|
|
$rs->{files}{$file}{instance_lines}{$rs->{t}{line}} = $rs->{t}{mod};
|
3060 |
|
|
push( @{$rs->{p}{instances}} , { module => $rs->{t}{mod} ,
|
3061 |
|
|
inst_name => $rs->{t}{name} ,
|
3062 |
|
|
file => $file,
|
3063 |
|
|
line => $rs->{t}{line},
|
3064 |
|
|
parameters => $rs->{t}{params},
|
3065 |
|
|
connections => {} });
|
3066 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$rs->{t}{line},
|
3067 |
|
|
$rs->{module}."_".$rs->{t}{name});',
|
3068 |
|
|
},
|
3069 |
|
|
INST_NAMED_PORT_CON_AFTER => {
|
3070 |
|
|
COMMA => 'my $inst_num= $#{$rs->{p}{instances}};
|
3071 |
|
|
$rs->{p}{instances}[$inst_num]{connections}{$portName}=$fromLastPos;
|
3072 |
|
|
if ($portName =~ /^[0-9]/ ) { # clear named_ports flag if port is a number
|
3073 |
|
|
$rs->{p}{named_ports} = 0;
|
3074 |
|
|
}
|
3075 |
|
|
else { # remove the bracket from the end if a named port
|
3076 |
|
|
$rs->{p}{instances}[$inst_num]{connections}{$portName}=~s/\)\s*$//s;
|
3077 |
|
|
}
|
3078 |
|
|
foreach my $s (@vids) {
|
3079 |
|
|
$self->_init_signal($rs->{p}{signals},$s->{name},"wire","","",$file,$s->{line},0,$rs->{t}{dimensions})
|
3080 |
|
|
&& _add_anchor($rs->{files}{$file}{anchors},$s->{line},"");
|
3081 |
|
|
push( @{$rs->{p}{signals}{$s->{name}}{port_con}},
|
3082 |
|
|
{ port => $portName ,
|
3083 |
|
|
line => $s->{line},
|
3084 |
|
|
file => $file,
|
3085 |
|
|
module => $rs->{t}{mod} ,
|
3086 |
|
|
inst => $rs->{t}{name} });
|
3087 |
|
|
}',
|
3088 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
3089 |
|
|
},
|
3090 |
|
|
INST_NUMBERED_PORT => {
|
3091 |
|
|
COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
3092 |
|
|
BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA',
|
3093 |
|
|
},
|
3094 |
|
|
IN_EVENT_BRACKET_EDGE => {
|
3095 |
|
|
VID => 'if (exists($rs->{p}{signals}{$match})) {
|
3096 |
|
|
$rs->{p}{signals}{$match}{$edgeType}=1; };',
|
3097 |
|
|
},
|
3098 |
|
|
|
3099 |
|
|
STMNT_ASSIGN_OR_TASK => { # copy of STMNT_ASSIGN
|
3100 |
|
|
EQUALS => 'foreach my $s (@vids) {
|
3101 |
|
|
my $sigp = undef;
|
3102 |
|
|
if ( exists($rs->{p}{signals}{$s->{name}} )) {
|
3103 |
|
|
$sigp = $rs->{p}{signals}{$s->{name}};
|
3104 |
|
|
}
|
3105 |
|
|
elsif ( exists($rs->{p}{m_signals}) &&
|
3106 |
|
|
exists($rs->{p}{m_signals}{$s->{name}}) ) {
|
3107 |
|
|
$sigp = $rs->{p}{m_signals}{$s->{name}};
|
3108 |
|
|
}
|
3109 |
|
|
if (defined($sigp) && ($sigp->{a_line}==-1)) {
|
3110 |
|
|
$sigp->{a_line}=$s->{line};
|
3111 |
|
|
$sigp->{a_file}=$file;
|
3112 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$s->{line},"");
|
3113 |
|
|
}
|
3114 |
|
|
}',
|
3115 |
|
|
},
|
3116 |
|
|
STMNT_ASSIGN => { # copy of STMNT_ASSIGN_OR_TASK
|
3117 |
|
|
EQUALS => 'STMNT_ASSIGN_OR_TASK:EQUALS',
|
3118 |
|
|
},
|
3119 |
|
|
ANSI_PORTS_SIGNAL_NAME => { # V2001 ansi ports
|
3120 |
|
|
VID => '$self->_init_signal($rs->{p}{signals},$match,$rs->{t}{type},$rs->{t}{type2},
|
3121 |
|
|
$rs->{t}{range},$file,$line,1,$rs->{t}{dimensions});
|
3122 |
|
|
push(@{$rs->{p}{port_order}},$match) if exists $rs->{p}{port_order};
|
3123 |
|
|
_add_anchor($rs->{files}{$file}{anchors},$line,"");',
|
3124 |
|
|
},
|
3125 |
|
|
};
|
3126 |
|
|
|
3127 |
|
|
############################################################
|
3128 |
|
|
# language definition
|
3129 |
|
|
############################################################
|
3130 |
|
|
|
3131 |
|
|
$vid_vnum_or_string =
|
3132 |
|
|
[ { arcName=> 'HVID', regexp=> '$HVID', nextState=> ['$ps->{curState}'] ,}, # hier id
|
3133 |
|
|
{ arcName=> 'VID', regexp=> '$VID' , nextState=> ['$ps->{curState}'] ,},
|
3134 |
|
|
{ arcName=> 'NUMBER', regexp=> '$VNUM', nextState=> ['$ps->{curState}'] ,},
|
3135 |
|
|
{ arcName=> 'STRING', regexp=> '\\"', nextState=> ['IN_STRING','$ps->{curState}'],},
|
3136 |
|
|
];
|
3137 |
|
|
|
3138 |
|
|
$languageDef =
|
3139 |
|
|
[
|
3140 |
|
|
{
|
3141 |
|
|
stateName => 'START',
|
3142 |
|
|
confusedNextState => 'START',
|
3143 |
|
|
search =>
|
3144 |
|
|
[
|
3145 |
|
|
{ arcName => 'MODULE' , regexp => '\b(?:module|macromodule|primitive)\b',
|
3146 |
|
|
nextState => ['MODULE_NAME'] ,},
|
3147 |
|
|
{ arcName => 'CONFIG', regexp => '\bconfig\b', # V2001
|
3148 |
|
|
nextState => ['CONFIG'] , },
|
3149 |
|
|
{ arcName => 'LIBRARY', regexp => '\blibrary\b', # V2001
|
3150 |
|
|
nextState => ['LIBRARY'] , },
|
3151 |
|
|
],
|
3152 |
|
|
},
|
3153 |
|
|
{
|
3154 |
|
|
stateName => 'MODULE',
|
3155 |
|
|
confusedNextState => 'MODULE',
|
3156 |
|
|
search =>
|
3157 |
|
|
[
|
3158 |
|
|
{ arcName => 'ENDMODULE' , regexp => '\b(?:end(?:module|primitive))\b',
|
3159 |
|
|
nextState => ['START'] , },
|
3160 |
|
|
{ arcName => 'FUNCTION', regexp => '\bfunction\b',
|
3161 |
|
|
nextState => ['FUNCTION'] , },
|
3162 |
|
|
{ arcName => 'TASK', regexp => '\btask\b',
|
3163 |
|
|
nextState => ['TASK'] , },
|
3164 |
|
|
{ arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm
|
3165 |
|
|
nextState => ['PARAM_TYPE','MODULE'] , },
|
3166 |
|
|
{ arcName => 'SPECIFY', regexp => '\bspecify\b',
|
3167 |
|
|
nextState => ['SPECIFY'] , },
|
3168 |
|
|
{ arcName => 'TABLE', regexp => '\btable\b',
|
3169 |
|
|
nextState => ['TABLE'] , },
|
3170 |
|
|
{ arcName => 'EVENT_DECLARATION' , regexp => '\bevent\b' ,
|
3171 |
|
|
nextState => ['EVENT_DECLARATION'] , },
|
3172 |
|
|
{ arcName => 'DEFPARAM' , regexp => '\bdefparam\b' ,
|
3173 |
|
|
nextState => ['DEFPARAM'] , },
|
3174 |
|
|
{ arcName => 'GATE' , regexp => "$verilog_gatetype_regexp" ,
|
3175 |
|
|
nextState => ['GATE'] , },
|
3176 |
|
|
{ arcName => 'ASSIGN' , regexp => '\bassign\b' ,
|
3177 |
|
|
nextState => ['ASSIGN'] , },
|
3178 |
|
|
{ arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" ,
|
3179 |
|
|
nextState => ['DRIVE_STRENGTH','MODULE'] , },
|
3180 |
|
|
{ arcName => 'INITIAL_OR_ALWAYS', regexp => '\b(?:initial|always)\b' ,
|
3181 |
|
|
nextState => ['STMNT','MODULE'] , },
|
3182 |
|
|
{ arcName => 'GENERATE', regexp => '\bgenerate\b', # V2001
|
3183 |
|
|
nextState => ['GENERATE'] , },
|
3184 |
|
|
|
3185 |
|
|
|
3186 |
|
|
{ arcName => 'INST', regexp => '$VID' ,
|
3187 |
|
|
nextState => ['INST_PARAM'] , },
|
3188 |
|
|
# don't put any more states here because $VID matches almost anything
|
3189 |
|
|
],
|
3190 |
|
|
},################ END OF MODULE STATE
|
3191 |
|
|
{
|
3192 |
|
|
stateName => 'MODULE_NAME',
|
3193 |
|
|
search => # $nState is usually MODULE_PPL, but is set to
|
3194 |
|
|
# IGNORE_MODULE when a duplicate module is found
|
3195 |
|
|
[ { arcName => 'NAME', regexp => '$VID' , nextState => ['$nState'] , }, ],
|
3196 |
|
|
},
|
3197 |
|
|
{
|
3198 |
|
|
stateName => 'IGNORE_MODULE' , # just look for endmodule
|
3199 |
|
|
allowAnything => 1,
|
3200 |
|
|
search => [
|
3201 |
|
|
{ arcName => 'ENDMODULE' , regexp => '\bendmodule\b',
|
3202 |
|
|
nextState => ['START'], },
|
3203 |
|
|
@$vid_vnum_or_string,
|
3204 |
|
|
],
|
3205 |
|
|
},
|
3206 |
|
|
{
|
3207 |
|
|
stateName => 'MODULE_PPL' , # v2001 module_parameter_port_list (A.1.3)
|
3208 |
|
|
failNextState => ['MODULE_PORTS'],
|
3209 |
|
|
search => [ { regexp => '#', nextState => ['PPL_BRACKET'], }, ],
|
3210 |
|
|
},
|
3211 |
|
|
{
|
3212 |
|
|
stateName => 'MODULE_PORTS' , # just look for signals until ;
|
3213 |
|
|
allowAnything => 1,
|
3214 |
|
|
search => [
|
3215 |
|
|
{ arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b', # V2001 ansi ports
|
3216 |
|
|
nextState => ['ANSI_PORTS_TYPE','MODULE'], resetPos => 1, },
|
3217 |
|
|
{ arcName => 'END', regexp => ';' , nextState => ['MODULE'] , },
|
3218 |
|
|
@$vid_vnum_or_string,
|
3219 |
|
|
],
|
3220 |
|
|
},
|
3221 |
|
|
{
|
3222 |
|
|
stateName => 'FUNCTION' ,
|
3223 |
|
|
search => [
|
3224 |
|
|
{ arcName => 'RANGE', regexp => '\[', nextState => ['IN_RANGE','FUNCTION'] , },
|
3225 |
|
|
{ arcName => 'TYPE', regexp => '\b(?:real|integer|time|realtime)\b',
|
3226 |
|
|
nextState => ['FUNCTION'] , },
|
3227 |
|
|
{ arcName => 'SIGNED', regexp => '\bsigned\b' ,nextState => ['FUNCTION'] , }, # V2001
|
3228 |
|
|
{ arcName => 'AUTO', regexp => '\bautomatic\b' ,nextState => ['FUNCTION'] , }, # V2001
|
3229 |
|
|
{ arcName => 'NAME', regexp => '$VID' , nextState => ['FUNCTION_AFTER_NAME'] ,
|
3230 |
|
|
},
|
3231 |
|
|
],
|
3232 |
|
|
},
|
3233 |
|
|
{
|
3234 |
|
|
stateName => 'FUNCTION_AFTER_NAME' ,
|
3235 |
|
|
search => [
|
3236 |
|
|
{ arcName => 'SEMICOLON', regexp => ';', nextState => ['F_SIGNAL'] , },
|
3237 |
|
|
{ arcName => 'BRACKET', regexp => '\(' , # V2001
|
3238 |
|
|
nextState => ['ANSI_PORTS_TYPE','F_SIGNAL'] , },
|
3239 |
|
|
],
|
3240 |
|
|
},
|
3241 |
|
|
{
|
3242 |
|
|
stateName => 'TASK' ,
|
3243 |
|
|
search => [
|
3244 |
|
|
{ arcName => 'AUTO', regexp => '\bautomatic\b', nextState => ['TASK'],}, # V2001
|
3245 |
|
|
{ arcName => 'NAME', regexp => '$VID', nextState => ['TASK_AFTER_NAME'],},],
|
3246 |
|
|
},
|
3247 |
|
|
{
|
3248 |
|
|
stateName => 'TASK_AFTER_NAME' ,
|
3249 |
|
|
search => [
|
3250 |
|
|
{ arcName => 'SEMICOLON', regexp => ';', nextState => ['T_SIGNAL'] , },
|
3251 |
|
|
{ arcName => 'BRACKET', regexp => '\(' , # V2001
|
3252 |
|
|
nextState => ['ANSI_PORTS_TYPE','T_SIGNAL'] , },
|
3253 |
|
|
],
|
3254 |
|
|
},
|
3255 |
|
|
{
|
3256 |
|
|
stateName => 'T_SIGNAL' ,
|
3257 |
|
|
failNextState => ['STMNT','ENDTASK'],
|
3258 |
|
|
search => [
|
3259 |
|
|
{ arcName => 'ENDTASK', regexp => '\bendtask\b',
|
3260 |
|
|
nextState => ['MODULE'] , },
|
3261 |
|
|
{ arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" ,
|
3262 |
|
|
nextState => ['DRIVE_STRENGTH','T_SIGNAL'] , },
|
3263 |
|
|
{ arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm
|
3264 |
|
|
nextState => ['PARAM_TYPE','T_SIGNAL'] , },
|
3265 |
|
|
],
|
3266 |
|
|
},
|
3267 |
|
|
{
|
3268 |
|
|
stateName => 'ENDTASK',
|
3269 |
|
|
search => [
|
3270 |
|
|
{ arcName => 'ENDTASK', regexp => '\bendtask\b',
|
3271 |
|
|
nextState => ['MODULE'] , },
|
3272 |
|
|
],
|
3273 |
|
|
},
|
3274 |
|
|
{
|
3275 |
|
|
stateName => 'F_SIGNAL' ,
|
3276 |
|
|
failNextState => ['STMNT','ENDFUNCTION'],
|
3277 |
|
|
search => [
|
3278 |
|
|
{ arcName => 'ENDFUNCTION', regexp => '\bendfunction\b',
|
3279 |
|
|
nextState => ['MODULE'] , },
|
3280 |
|
|
{ arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" ,
|
3281 |
|
|
nextState => ['DRIVE_STRENGTH','F_SIGNAL'] , },
|
3282 |
|
|
{ arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm
|
3283 |
|
|
nextState => ['PARAM_TYPE','F_SIGNAL'] , },
|
3284 |
|
|
],
|
3285 |
|
|
},
|
3286 |
|
|
{
|
3287 |
|
|
stateName => 'ENDFUNCTION',
|
3288 |
|
|
search => [
|
3289 |
|
|
{ arcName => 'ENDFUNCTION', regexp => '\bendfunction\b',
|
3290 |
|
|
nextState => ['MODULE'] , },
|
3291 |
|
|
],
|
3292 |
|
|
},
|
3293 |
|
|
{
|
3294 |
|
|
stateName => 'PARAM_TYPE',
|
3295 |
|
|
failNextState => ['PARAM_NAME'],
|
3296 |
|
|
search => [
|
3297 |
|
|
{ arcName => 'RANGE', regexp => '\[' ,
|
3298 |
|
|
nextState => ['IN_RANGE','PARAM_NAME'] , },
|
3299 |
|
|
{ arcName => 'SIGNED', regexp => '\bsigned\b' ,
|
3300 |
|
|
nextState => ['PARAM_TYPE'] , }, # may be followed by a range
|
3301 |
|
|
{ arcName => 'OTHER', regexp => '\b(?:integer|real|realtime|time)\b' ,
|
3302 |
|
|
nextState => ['PARAM_NAME'] , },
|
3303 |
|
|
],
|
3304 |
|
|
},
|
3305 |
|
|
{
|
3306 |
|
|
stateName => 'PARAM_NAME',
|
3307 |
|
|
search => [
|
3308 |
|
|
{ arcName => 'NAME', regexp => '$VID' ,
|
3309 |
|
|
nextState => ['PARAMETER_EQUAL','PARAM_AFTER_EQUALS'] , },
|
3310 |
|
|
],
|
3311 |
|
|
},
|
3312 |
|
|
{
|
3313 |
|
|
stateName => 'PARAMETER_EQUAL',
|
3314 |
|
|
search => [ { regexp => '=' , storePos => 1, }, ]
|
3315 |
|
|
},
|
3316 |
|
|
{
|
3317 |
|
|
stateName => 'PARAM_AFTER_EQUALS',
|
3318 |
|
|
allowAnything => 1,
|
3319 |
|
|
search =>
|
3320 |
|
|
[
|
3321 |
|
|
{ arcName => 'CONCAT', regexp => '{' ,
|
3322 |
|
|
nextState => ['IN_CONCAT','PARAM_AFTER_EQUALS'] , },
|
3323 |
|
|
{ arcName => 'COMMA', regexp => ',' ,
|
3324 |
|
|
nextState => ['PARAM_NAME'] , },
|
3325 |
|
|
{ arcName => 'SEMICOLON', regexp => ';' , },
|
3326 |
|
|
@$vid_vnum_or_string,
|
3327 |
|
|
]
|
3328 |
|
|
},
|
3329 |
|
|
{
|
3330 |
|
|
stateName => 'IN_CONCAT',
|
3331 |
|
|
allowAnything => 1,
|
3332 |
|
|
search =>
|
3333 |
|
|
[
|
3334 |
|
|
{ arcName => 'CONCAT' , regexp => '{' ,
|
3335 |
|
|
nextState => ['IN_CONCAT','IN_CONCAT'] , },
|
3336 |
|
|
{ arcName => 'END' , regexp => '}' , }, # pop up
|
3337 |
|
|
@$vid_vnum_or_string,
|
3338 |
|
|
]
|
3339 |
|
|
},
|
3340 |
|
|
{
|
3341 |
|
|
stateName => 'IN_RANGE',
|
3342 |
|
|
allowAnything => 1,
|
3343 |
|
|
search =>
|
3344 |
|
|
[
|
3345 |
|
|
{ arcName => 'RANGE' , regexp => '\[' ,
|
3346 |
|
|
nextState => ['IN_RANGE','IN_RANGE'] , },
|
3347 |
|
|
{ arcName => 'END' , regexp => '\]' , }, # pop up
|
3348 |
|
|
@$vid_vnum_or_string,
|
3349 |
|
|
]
|
3350 |
|
|
},
|
3351 |
|
|
{
|
3352 |
|
|
stateName => 'IN_SIG_RANGE', # just like in range, but stores
|
3353 |
|
|
allowAnything => 1,
|
3354 |
|
|
search =>
|
3355 |
|
|
[
|
3356 |
|
|
{ arcName => 'RANGE' , regexp => '\[' ,
|
3357 |
|
|
nextState => ['IN_SIG_RANGE','IN_SIG_RANGE'] , },
|
3358 |
|
|
{ arcName => 'END' , regexp => '\]' , }, # pop up
|
3359 |
|
|
@$vid_vnum_or_string,
|
3360 |
|
|
]
|
3361 |
|
|
},
|
3362 |
|
|
{
|
3363 |
|
|
stateName => 'IN_MEM_RANGE', # just like in range, but stores
|
3364 |
|
|
allowAnything => 1,
|
3365 |
|
|
search =>
|
3366 |
|
|
[
|
3367 |
|
|
{ arcName => 'RANGE' , regexp => '\[' ,
|
3368 |
|
|
nextState => ['IN_MEM_RANGE','IN_MEM_RANGE'] , },
|
3369 |
|
|
{ arcName => 'END' , regexp => '\]' , }, # pop up
|
3370 |
|
|
@$vid_vnum_or_string,
|
3371 |
|
|
]
|
3372 |
|
|
},
|
3373 |
|
|
{
|
3374 |
|
|
stateName => 'IN_BRACKET',
|
3375 |
|
|
allowAnything => 1,
|
3376 |
|
|
search =>
|
3377 |
|
|
[
|
3378 |
|
|
{ arcName => 'BRACKET' , regexp => '\(' ,
|
3379 |
|
|
nextState => ['IN_BRACKET','IN_BRACKET'] , },
|
3380 |
|
|
{ arcName => 'END' , regexp => '\)' , }, # pop up
|
3381 |
|
|
@$vid_vnum_or_string,
|
3382 |
|
|
]
|
3383 |
|
|
},
|
3384 |
|
|
{
|
3385 |
|
|
stateName => 'IN_STRING',
|
3386 |
|
|
allowAnything => 1,
|
3387 |
|
|
search =>
|
3388 |
|
|
[ # note: put \" in regexp so that emacs colouring doesn't get confused
|
3389 |
|
|
{ arcName => 'ESCAPED_QUOTE' , regexp => '\\\\\\"' , # match \"
|
3390 |
|
|
nextState => ['IN_STRING'] , },
|
3391 |
|
|
# match \\ (to make sure that \\" does not match \"
|
3392 |
|
|
{ arcName => 'ESCAPE' , regexp => '\\\\\\\\' ,
|
3393 |
|
|
nextState => ['IN_STRING'] , },
|
3394 |
|
|
{ arcName => 'END' , regexp => '\\"' , }, # match " and pop up
|
3395 |
|
|
]
|
3396 |
|
|
},
|
3397 |
|
|
{
|
3398 |
|
|
stateName => 'SPECIFY',
|
3399 |
|
|
allowAnything => 1,
|
3400 |
|
|
search => [ { regexp => '\bendspecify\b' , nextState => ['MODULE'] ,},
|
3401 |
|
|
@$vid_vnum_or_string,],
|
3402 |
|
|
},
|
3403 |
|
|
{
|
3404 |
|
|
stateName => 'TABLE',
|
3405 |
|
|
allowAnything => 1,
|
3406 |
|
|
search => [ { regexp => '\bendtable\b' , nextState => ['MODULE'] ,},
|
3407 |
|
|
@$vid_vnum_or_string,],
|
3408 |
|
|
},
|
3409 |
|
|
{
|
3410 |
|
|
stateName => 'EVENT_DECLARATION' , # just look for ;
|
3411 |
|
|
allowAnything => 1,
|
3412 |
|
|
search => [ { regexp => ';' , nextState => ['MODULE'] , },
|
3413 |
|
|
@$vid_vnum_or_string,],
|
3414 |
|
|
},
|
3415 |
|
|
{
|
3416 |
|
|
stateName => 'DEFPARAM' , # just look for ;
|
3417 |
|
|
allowAnything => 1,
|
3418 |
|
|
search => [ { regexp => ';' , nextState => ['MODULE'] , },
|
3419 |
|
|
@$vid_vnum_or_string,],
|
3420 |
|
|
},
|
3421 |
|
|
{
|
3422 |
|
|
# REVISIT: could find signal driven by gate here (is output always the first one??)
|
3423 |
|
|
stateName => 'GATE' ,
|
3424 |
|
|
allowAnything => 1,
|
3425 |
|
|
search => [ { regexp => ';' , nextState => ['MODULE'] , },
|
3426 |
|
|
@$vid_vnum_or_string,],
|
3427 |
|
|
},
|
3428 |
|
|
{
|
3429 |
|
|
stateName => 'ASSIGN',
|
3430 |
|
|
allowAnything => 1,
|
3431 |
|
|
search =>
|
3432 |
|
|
[
|
3433 |
|
|
{ arcName => 'RANGE' , regexp => '\[' ,
|
3434 |
|
|
nextState => ['IN_RANGE','ASSIGN'] , },
|
3435 |
|
|
{ arcName => 'EQUALS' , regexp => '=' ,
|
3436 |
|
|
nextState => ['ASSIGN_AFTER_EQUALS'] , },
|
3437 |
|
|
@$vid_vnum_or_string,
|
3438 |
|
|
]
|
3439 |
|
|
},
|
3440 |
|
|
{
|
3441 |
|
|
stateName => 'ASSIGN_AFTER_EQUALS' ,
|
3442 |
|
|
allowAnything => 1,
|
3443 |
|
|
search =>
|
3444 |
|
|
[
|
3445 |
|
|
{ arcName=>'COMMA', regexp => ',',
|
3446 |
|
|
nextState => ['ASSIGN'],},
|
3447 |
|
|
{ arcName=>'CONCAT', regexp => '{',
|
3448 |
|
|
nextState => ['IN_CONCAT','ASSIGN_AFTER_EQUALS'],},
|
3449 |
|
|
# don't get confused by function calls (which can also contain commas)
|
3450 |
|
|
{ arcName=>'BRACKET', regexp => '\(',
|
3451 |
|
|
nextState => ['IN_BRACKET','ASSIGN_AFTER_EQUALS'],},
|
3452 |
|
|
{ arcName=>'END', regexp => ';',
|
3453 |
|
|
nextState => ['MODULE'],},
|
3454 |
|
|
@$vid_vnum_or_string,
|
3455 |
|
|
],
|
3456 |
|
|
},
|
3457 |
|
|
{
|
3458 |
|
|
stateName => 'DRIVE_STRENGTH', # signal defs - drive strength or charge strength
|
3459 |
|
|
failNextState => ['SCALARED_OR_VECTORED'],
|
3460 |
|
|
search => [ { regexp => '\(', nextState => ['IN_BRACKET','SCALARED_OR_VECTORED'],}],
|
3461 |
|
|
},
|
3462 |
|
|
{ # REVISIT: V2001 - the name of this is misleading now
|
3463 |
|
|
stateName => 'SCALARED_OR_VECTORED', # for signal defs
|
3464 |
|
|
failNextState => ['SIGNAL_RANGE'],
|
3465 |
|
|
search => [ { regexp => '\b(?:scalared|vectored)\b', nextState => ['SIGNAL_RANGE'],},
|
3466 |
|
|
{ arcName => 'TYPE' , regexp => "$verilog_sigs_regexp", # V2001
|
3467 |
|
|
nextState => ['SCALARED_OR_VECTORED'],},
|
3468 |
|
|
{ regexp => '\b(?:signed)\b', nextState => ['SCALARED_OR_VECTORED'],},], # V2001
|
3469 |
|
|
},
|
3470 |
|
|
{
|
3471 |
|
|
stateName => 'SIGNAL_RANGE', # for signal defs
|
3472 |
|
|
failNextState => ['SIGNAL_DELAY'],
|
3473 |
|
|
search => [ { regexp => '\[', nextState => ['IN_SIG_RANGE','SIGNAL_DELAY'],
|
3474 |
|
|
storePos => 1,}, ],
|
3475 |
|
|
},
|
3476 |
|
|
{
|
3477 |
|
|
stateName => 'SIGNAL_DELAY', # for signal defs
|
3478 |
|
|
failNextState => ['SIGNAL_NAME'],
|
3479 |
|
|
search => [ { regexp => '\#', nextState => ['DELAY_VALUE','SIGNAL_NAME'],}, ],
|
3480 |
|
|
},
|
3481 |
|
|
{
|
3482 |
|
|
stateName => 'SIGNAL_NAME', # for signal defs
|
3483 |
|
|
search => [ { arcName => 'VID' , regexp => '$VID',
|
3484 |
|
|
nextState => ['SIGNAL_AFTER_NAME'], }, ],
|
3485 |
|
|
},
|
3486 |
|
|
{ # for signal defs
|
3487 |
|
|
stateName => 'SIGNAL_AFTER_NAME',
|
3488 |
|
|
search =>
|
3489 |
|
|
[
|
3490 |
|
|
{ regexp => ',', nextState => ['SIGNAL_NAME'],},
|
3491 |
|
|
{ regexp => '\[', nextState => ['IN_MEM_RANGE','SIGNAL_AFTER_NAME'],
|
3492 |
|
|
storePos => 1 , }, # memories
|
3493 |
|
|
{ arcName => 'SEMICOLON' , regexp => ';',}, # pop up
|
3494 |
|
|
{ arcName => 'ASSIGN', regexp => '=', nextState => ['SIGNAL_AFTER_EQUALS'],}
|
3495 |
|
|
],
|
3496 |
|
|
},
|
3497 |
|
|
{
|
3498 |
|
|
stateName => 'SIGNAL_AFTER_EQUALS' ,
|
3499 |
|
|
allowAnything => 1,
|
3500 |
|
|
search =>
|
3501 |
|
|
[
|
3502 |
|
|
{ regexp => ',', nextState => ['SIGNAL_NAME'],},
|
3503 |
|
|
{ regexp => '{', nextState => ['IN_CONCAT','SIGNAL_AFTER_EQUALS'],},
|
3504 |
|
|
{ regexp => '\(', nextState => ['IN_BRACKET','SIGNAL_AFTER_EQUALS'],},
|
3505 |
|
|
{ arcName => 'END', regexp => ';', }, # pop up
|
3506 |
|
|
@$vid_vnum_or_string,
|
3507 |
|
|
],
|
3508 |
|
|
},
|
3509 |
|
|
{
|
3510 |
|
|
stateName => 'INST_PARAM',
|
3511 |
|
|
failNextState => ['INST_NAME'],
|
3512 |
|
|
search => [ { regexp => '\#', nextState=> ['INST_PARAM_BRACKET'],},],
|
3513 |
|
|
},
|
3514 |
|
|
{
|
3515 |
|
|
stateName => 'INST_PARAM_BRACKET',
|
3516 |
|
|
search => [ { arcName => 'BRACKET' ,
|
3517 |
|
|
regexp => '\(',
|
3518 |
|
|
storePos => 1,
|
3519 |
|
|
nextState=> ['INST_PARAM_VALUE'],},
|
3520 |
|
|
# this is here to catch and illegal case which DC accepts
|
3521 |
|
|
{ arcName => 'NO_BRACKET' ,
|
3522 |
|
|
regexp => '($VID|$VNUM)',
|
3523 |
|
|
nextState=> ['INST_NAME'],}, ],
|
3524 |
|
|
},
|
3525 |
|
|
{
|
3526 |
|
|
stateName => 'INST_PARAM_VALUE',
|
3527 |
|
|
allowAnything => 1,
|
3528 |
|
|
search => [
|
3529 |
|
|
{ regexp => '\(', nextState=> ['IN_BRACKET','INST_PARAM_VALUE'],},
|
3530 |
|
|
{ regexp => '\[', nextState => ['IN_RANGE','INST_PARAM_VALUE'],},
|
3531 |
|
|
{ regexp => '\{', nextState => ['IN_CONCAT','INST_PARAM_VALUE'],},
|
3532 |
|
|
{ arcName => 'COMMA' ,
|
3533 |
|
|
regexp => ',',
|
3534 |
|
|
storePos => 1,
|
3535 |
|
|
nextState=> ['INST_PARAM_VALUE'],},
|
3536 |
|
|
{ arcName => 'END' ,
|
3537 |
|
|
regexp => '\)',
|
3538 |
|
|
nextState=> ['INST_NAME'],},
|
3539 |
|
|
],
|
3540 |
|
|
},
|
3541 |
|
|
{
|
3542 |
|
|
stateName => 'INST_NAME',
|
3543 |
|
|
failNextState => ['INST_BRACKET'],
|
3544 |
|
|
search =>
|
3545 |
|
|
[
|
3546 |
|
|
{ arcName => 'VID' , regexp => '$VID',
|
3547 |
|
|
nextState => ['INST_RANGE'], },
|
3548 |
|
|
],
|
3549 |
|
|
},
|
3550 |
|
|
{
|
3551 |
|
|
stateName => 'INST_NO_NAME' ,
|
3552 |
|
|
allowAnything => 1,
|
3553 |
|
|
search => [ { regexp => ';' , }, @$vid_vnum_or_string,],
|
3554 |
|
|
},
|
3555 |
|
|
{
|
3556 |
|
|
stateName => 'INST_RANGE',
|
3557 |
|
|
failNextState => ['INST_BRACKET'],
|
3558 |
|
|
search => [ { regexp => '\[', nextState => ['IN_RANGE','INST_BRACKET'],}, ],
|
3559 |
|
|
},
|
3560 |
|
|
{
|
3561 |
|
|
stateName => 'INST_BRACKET',
|
3562 |
|
|
search => [ { arcName => 'PORTS' , regexp => '\(', nextState => ['INST_PORTS'],},],
|
3563 |
|
|
},
|
3564 |
|
|
{
|
3565 |
|
|
stateName => 'INST_PORTS',
|
3566 |
|
|
failNextState => ['INST_NUMBERED_PORT'],
|
3567 |
|
|
failStorePos => 1,
|
3568 |
|
|
search =>
|
3569 |
|
|
[
|
3570 |
|
|
{ arcName => 'COMMA', regexp => ',', nextState => ['INST_PORTS'], },
|
3571 |
|
|
{ regexp => '\.', nextState => ['INST_PORT_NAME'], },
|
3572 |
|
|
{ regexp => '\)', nextState => ['AFTER_INST'], },
|
3573 |
|
|
],
|
3574 |
|
|
},
|
3575 |
|
|
{
|
3576 |
|
|
stateName => 'INST_PORT_NAME',
|
3577 |
|
|
search => [ { arcName => 'NAME' , regexp => '$VID',
|
3578 |
|
|
nextState => ['INST_NAMED_PORT_BRACKET','INST_NAMED_PORT_CON',
|
3579 |
|
|
'INST_NAMED_PORT_CON_AFTER'], }, ],
|
3580 |
|
|
},
|
3581 |
|
|
{
|
3582 |
|
|
stateName => 'INST_NAMED_PORT_BRACKET',
|
3583 |
|
|
search => [ { regexp => '\(' , storePos => 1, },]
|
3584 |
|
|
},
|
3585 |
|
|
{
|
3586 |
|
|
stateName => 'INST_NAMED_PORT_CON',
|
3587 |
|
|
allowAnything => 1,
|
3588 |
|
|
search =>
|
3589 |
|
|
[
|
3590 |
|
|
{ regexp => '\[' , nextState => ['IN_RANGE','INST_NAMED_PORT_CON'] , },
|
3591 |
|
|
{ regexp => '\{' , nextState => ['IN_CONCAT','INST_NAMED_PORT_CON'] , },
|
3592 |
|
|
{ regexp => '\(' ,
|
3593 |
|
|
nextState => ['INST_NAMED_PORT_CON','INST_NAMED_PORT_CON'], },
|
3594 |
|
|
{ arcName => 'END', regexp => '\)' , }, # pop up
|
3595 |
|
|
@$vid_vnum_or_string,
|
3596 |
|
|
]
|
3597 |
|
|
},
|
3598 |
|
|
{
|
3599 |
|
|
stateName => 'INST_NAMED_PORT_CON_AFTER',
|
3600 |
|
|
search =>
|
3601 |
|
|
[
|
3602 |
|
|
{ arcName => 'BRACKET', regexp => '\)' ,
|
3603 |
|
|
nextState => ['AFTER_INST']},
|
3604 |
|
|
{ arcName => 'COMMA' , regexp => ',' ,
|
3605 |
|
|
nextState => ['INST_DOT']},
|
3606 |
|
|
]
|
3607 |
|
|
},
|
3608 |
|
|
{ stateName => 'INST_DOT',
|
3609 |
|
|
search =>
|
3610 |
|
|
[
|
3611 |
|
|
{ regexp => '\.' , nextState => ['INST_PORT_NAME']},
|
3612 |
|
|
{ regexp => ',' , nextState => ['INST_DOT']}, # blank port
|
3613 |
|
|
]
|
3614 |
|
|
},
|
3615 |
|
|
{
|
3616 |
|
|
stateName => 'INST_NUMBERED_PORT',
|
3617 |
|
|
allowAnything => 1,
|
3618 |
|
|
search =>
|
3619 |
|
|
[
|
3620 |
|
|
{ regexp => '\[', nextState => ['IN_RANGE','INST_NUMBERED_PORT'],},
|
3621 |
|
|
{ regexp => '\{', nextState => ['IN_CONCAT','INST_NUMBERED_PORT'],},
|
3622 |
|
|
{ regexp => '\(', nextState => ['IN_BRACKET','INST_NUMBERED_PORT'],},
|
3623 |
|
|
{ arcName => 'BRACKET' , regexp => '\)', nextState => ['AFTER_INST'], },
|
3624 |
|
|
{ arcName => 'COMMA' , regexp => ',' , nextState => ['INST_NUMBERED_PORT'],
|
3625 |
|
|
storePos => 1, },
|
3626 |
|
|
@$vid_vnum_or_string,
|
3627 |
|
|
]
|
3628 |
|
|
},
|
3629 |
|
|
{ stateName => 'AFTER_INST',
|
3630 |
|
|
search => [
|
3631 |
|
|
{ arcName => 'SEMICOLON', regexp => ';', nextState => ['MODULE'], },
|
3632 |
|
|
{ arcName => 'COMMA', regexp => ',', nextState => ['INST_NAME'], },
|
3633 |
|
|
]
|
3634 |
|
|
},
|
3635 |
|
|
{
|
3636 |
|
|
stateName => 'STMNT',
|
3637 |
|
|
search =>
|
3638 |
|
|
[
|
3639 |
|
|
{ arcName => 'IF', regexp => '\bif\b' ,
|
3640 |
|
|
nextState => ['BRACKET','IN_BRACKET','STMNT','MAYBE_ELSE'] ,},
|
3641 |
|
|
{ arcName => 'REPEAT_WHILE_FOR_WAIT', regexp => '\b(?:repeat|while|for|wait)\b' ,
|
3642 |
|
|
nextState => ['BRACKET','IN_BRACKET','STMNT'] , },
|
3643 |
|
|
{ arcName => 'FOREVER', regexp => '\bforever\b' ,
|
3644 |
|
|
nextState => ['STMNT'] , },
|
3645 |
|
|
{ arcName => 'CASE', regexp => '\bcase[xz]?\b' ,
|
3646 |
|
|
nextState => ['BRACKET','IN_BRACKET','CASE_ITEM'] , },
|
3647 |
|
|
{ arcName => 'BEGIN', regexp => '\bbegin\b' ,
|
3648 |
|
|
nextState => ['BLOCK_NAME','IN_SEQ_BLOCK'] , },
|
3649 |
|
|
{ arcName => 'FORK', regexp => '\bfork\b' ,
|
3650 |
|
|
nextState => ['BLOCK_NAME','IN_PAR_BLOCK'] , },
|
3651 |
|
|
{ arcName => 'DELAY', regexp => '\#' ,
|
3652 |
|
|
nextState => ['DELAY_VALUE','STMNT'] , },
|
3653 |
|
|
{ arcName => 'EVENT_CONTROL', regexp => '\@' ,
|
3654 |
|
|
nextState => ['EVENT_CONTROL'] , },
|
3655 |
|
|
{ arcName => 'SYSTEM_TASK', regexp => '\$$VID' ,
|
3656 |
|
|
nextState => ['SYSTEM_TASK'] , },
|
3657 |
|
|
{ arcName => 'DISABLE_ASSIGN_DEASSIGN_FORCE_RELEASE',
|
3658 |
|
|
regexp => '\b(?:disable|assign|deassign|force|release)\b',
|
3659 |
|
|
nextState => ['STMNT_JUNK_TO_SEMICOLON'] , }, # just throw stuff away
|
3660 |
|
|
# a assignment to a hierarchical thing mustn't collect the vid
|
3661 |
|
|
# like a normal assign as hierarchical nets/signals will confuse downstream code
|
3662 |
|
|
{ arcName => 'HIER_ASSIGN_OR_TASK', regexp => '$HVID' ,
|
3663 |
|
|
nextState => ['STMNT_ASSIGN_OR_TASK'] , },
|
3664 |
|
|
{ arcName => 'ASSIGN_OR_TASK', regexp => '$VID' ,
|
3665 |
|
|
nextState => ['STMNT_ASSIGN_OR_TASK'] , },
|
3666 |
|
|
{ arcName => 'CONCAT', regexp => '{' ,
|
3667 |
|
|
nextState => ['IN_CONCAT','STMNT_ASSIGN'] , },
|
3668 |
|
|
{ arcName => 'NULL', regexp => ';' ,
|
3669 |
|
|
}, # pop up
|
3670 |
|
|
{ arcName => 'POINTY_THING', regexp => '->' , # not sure what this is!
|
3671 |
|
|
nextState => ['POINTY_THING_NAME'] , },
|
3672 |
|
|
],
|
3673 |
|
|
},
|
3674 |
|
|
{
|
3675 |
|
|
stateName => 'MAYBE_ELSE',
|
3676 |
|
|
failNextState => [] , # don't get confused, just pop the stack for the next state
|
3677 |
|
|
search => [{ arcName => 'ELSE', regexp => '\belse\b' , nextState => ['STMNT'],},]
|
3678 |
|
|
},
|
3679 |
|
|
{
|
3680 |
|
|
stateName => 'BLOCK_NAME',
|
3681 |
|
|
failNextState => [] , # don't get confused, just pop the stack for the next state
|
3682 |
|
|
search => [{ arcName => 'COLON', regexp => ':' ,
|
3683 |
|
|
nextState => ['BLOCK_NAME_AFTER_COLON'] ,},]
|
3684 |
|
|
},
|
3685 |
|
|
{
|
3686 |
|
|
stateName => 'BLOCK_NAME_AFTER_COLON',
|
3687 |
|
|
search => [ { arcName => 'VID', regexp => '$VID' , nextState => ['BLOCK_SIGNAL'],}, ]
|
3688 |
|
|
},
|
3689 |
|
|
{
|
3690 |
|
|
stateName => 'BLOCK_SIGNAL' ,
|
3691 |
|
|
failNextState => [], # don't get confused, just pop the stack for the next state
|
3692 |
|
|
search => [
|
3693 |
|
|
{ arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" ,
|
3694 |
|
|
nextState => ['DRIVE_STRENGTH','BLOCK_SIGNAL'] , },
|
3695 |
|
|
],
|
3696 |
|
|
},
|
3697 |
|
|
|
3698 |
|
|
|
3699 |
|
|
{
|
3700 |
|
|
stateName => 'IN_SEQ_BLOCK',
|
3701 |
|
|
failNextState => ['STMNT','IN_SEQ_BLOCK'] ,
|
3702 |
|
|
search => [{ arcName => 'END', regexp => '\bend\b' , }, ]
|
3703 |
|
|
},
|
3704 |
|
|
{
|
3705 |
|
|
stateName => 'IN_PAR_BLOCK',
|
3706 |
|
|
failNextState => ['STMNT','IN_PAR_BLOCK'] ,
|
3707 |
|
|
search => [{ arcName => 'JOIN', regexp => '\bjoin\b' , }, ]
|
3708 |
|
|
},
|
3709 |
|
|
{
|
3710 |
|
|
stateName => 'DELAY_VALUE',
|
3711 |
|
|
search =>
|
3712 |
|
|
[{ arcName => 'NUMBER', regexp => '$VNUM', nextState => ['DELAY_COLON1'] },
|
3713 |
|
|
{ arcName => 'ID', regexp => '$VID', nextState => ['DELAY_COLON1'], },
|
3714 |
|
|
{ arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','DELAY_COLON1'],},]
|
3715 |
|
|
},
|
3716 |
|
|
{
|
3717 |
|
|
stateName => 'DELAY_COLON1',
|
3718 |
|
|
failNextState => [] , # popup
|
3719 |
|
|
search => [{ arcName => 'COLON', regexp => ':' , nextState => ['DELAY_VALUE2'] },]
|
3720 |
|
|
},
|
3721 |
|
|
{
|
3722 |
|
|
stateName => 'DELAY_VALUE2',
|
3723 |
|
|
search =>
|
3724 |
|
|
[{ arcName => 'NUMBER', regexp => '$VNUM', nextState => ['DELAY_COLON2'] },
|
3725 |
|
|
{ arcName => 'ID', regexp => '$VID', nextState => ['DELAY_COLON2'], },
|
3726 |
|
|
{ arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','DELAY_COLON2'],},]
|
3727 |
|
|
},
|
3728 |
|
|
{
|
3729 |
|
|
stateName => 'DELAY_COLON2',
|
3730 |
|
|
search => [{ arcName => 'COLON', regexp => ':' , nextState => ['DELAY_VALUE3'] },]
|
3731 |
|
|
},
|
3732 |
|
|
{
|
3733 |
|
|
stateName => 'DELAY_VALUE3',
|
3734 |
|
|
search =>
|
3735 |
|
|
[{ arcName => 'NUMBER', regexp => '$VNUM', },
|
3736 |
|
|
{ arcName => 'ID', regexp => '$VID', },
|
3737 |
|
|
{ arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET'],}, ]
|
3738 |
|
|
},
|
3739 |
|
|
{
|
3740 |
|
|
stateName => 'EVENT_CONTROL',
|
3741 |
|
|
search =>
|
3742 |
|
|
[
|
3743 |
|
|
{ arcName => 'ID', regexp => '(?:$HVID|$VID)', nextState => ['STMNT'], },
|
3744 |
|
|
{ arcName => 'STAR', regexp => '\*', nextState => ['STMNT'], }, # V2001
|
3745 |
|
|
{ arcName => 'BRACKET', regexp => '\(',
|
3746 |
|
|
nextState => ['IN_EVENT_BRACKET','STMNT'], },
|
3747 |
|
|
]
|
3748 |
|
|
},
|
3749 |
|
|
{
|
3750 |
|
|
stateName => 'IN_EVENT_BRACKET',
|
3751 |
|
|
allowAnything => 1,
|
3752 |
|
|
search =>
|
3753 |
|
|
[
|
3754 |
|
|
# must go before vid_vnum_or_string as posedge and negedge look like VIDs
|
3755 |
|
|
{ arcName => 'EDGE' , regexp => '\b(?:posedge|negedge)\b' ,
|
3756 |
|
|
nextState => ['IN_EVENT_BRACKET_EDGE'] , },
|
3757 |
|
|
{ arcName => 'BRACKET' , regexp => '\(' ,
|
3758 |
|
|
nextState => ['IN_EVENT_BRACKET','IN_EVENT_BRACKET'] , },
|
3759 |
|
|
{ arcName => 'STAR', regexp => '\*', nextState => ['IN_EVENT_BRACKET'], }, # V2001
|
3760 |
|
|
{ arcName => 'END' , regexp => '\)' , }, # popup
|
3761 |
|
|
@$vid_vnum_or_string,
|
3762 |
|
|
]
|
3763 |
|
|
},
|
3764 |
|
|
{ # in theory there could be an expression here, I just take the first VID
|
3765 |
|
|
stateName => 'IN_EVENT_BRACKET_EDGE',
|
3766 |
|
|
failNextState => ['IN_EVENT_BRACKET'] ,
|
3767 |
|
|
search => [{ arcName => 'VID', regexp => '$VID', nextState => ['IN_EVENT_BRACKET'],},],
|
3768 |
|
|
},
|
3769 |
|
|
{
|
3770 |
|
|
stateName => 'STMNT_ASSIGN_OR_TASK',
|
3771 |
|
|
failNextState => ['STMNT_SEMICOLON'],
|
3772 |
|
|
search =>
|
3773 |
|
|
[
|
3774 |
|
|
{ arcName => 'EQUALS', regexp => '[<]?=',
|
3775 |
|
|
nextState => ['STMNT_JUNK_TO_SEMICOLON'], },
|
3776 |
|
|
{ arcName => 'RANGE', regexp => '\[',
|
3777 |
|
|
nextState => ['IN_RANGE','STMNT_ASSIGN'],},
|
3778 |
|
|
{ arcName => 'BRACKET', regexp => '\(', # task with params
|
3779 |
|
|
nextState => ['IN_BRACKET','STMNT_SEMICOLON'], },
|
3780 |
|
|
]
|
3781 |
|
|
},
|
3782 |
|
|
{
|
3783 |
|
|
stateName => 'STMNT_ASSIGN',
|
3784 |
|
|
search =>
|
3785 |
|
|
[
|
3786 |
|
|
{ arcName => 'EQUALS', regexp => '[<]?=',
|
3787 |
|
|
nextState => ['STMNT_JUNK_TO_SEMICOLON'],},
|
3788 |
|
|
{ arcName => 'RANGE', regexp => '\[',
|
3789 |
|
|
nextState => ['IN_RANGE','STMNT_ASSIGN'],},
|
3790 |
|
|
],
|
3791 |
|
|
},
|
3792 |
|
|
{
|
3793 |
|
|
stateName => 'SYSTEM_TASK',
|
3794 |
|
|
failNextState => ['STMNT_SEMICOLON'],
|
3795 |
|
|
search =>
|
3796 |
|
|
[
|
3797 |
|
|
{ arcName => 'BRACKET', regexp => '\(',
|
3798 |
|
|
nextState => ['IN_BRACKET','STMNT_SEMICOLON'], }, ],
|
3799 |
|
|
},
|
3800 |
|
|
{
|
3801 |
|
|
stateName => 'POINTY_THING_NAME',
|
3802 |
|
|
search => [{ arcName => 'VID', regexp => '(?:$HVID|$VID)', nextState => ['STMNT_SEMICOLON'], }, ],
|
3803 |
|
|
},
|
3804 |
|
|
{
|
3805 |
|
|
stateName => 'CASE_ITEM',
|
3806 |
|
|
allowAnything => 1,
|
3807 |
|
|
search =>
|
3808 |
|
|
[
|
3809 |
|
|
{ arcName => 'END', regexp => '\bendcase\b', },
|
3810 |
|
|
{ arcName => 'COLON', regexp => ':',
|
3811 |
|
|
nextState => ['STMNT','CASE_ITEM'], },
|
3812 |
|
|
{ arcName => 'DEFAULT', regexp => '\bdefault\b',
|
3813 |
|
|
nextState => ['MAYBE_COLON','STMNT','CASE_ITEM'], },
|
3814 |
|
|
# don't get confused by colons in ranges
|
3815 |
|
|
{ arcName => 'RANGE', regexp => '\[',
|
3816 |
|
|
nextState => ['IN_RANGE','CASE_ITEM'], },
|
3817 |
|
|
@$vid_vnum_or_string,
|
3818 |
|
|
],
|
3819 |
|
|
},
|
3820 |
|
|
{
|
3821 |
|
|
stateName => 'MAYBE_COLON',
|
3822 |
|
|
failNextState => [],
|
3823 |
|
|
search => [ { regexp => ':' , }, ]
|
3824 |
|
|
},
|
3825 |
|
|
{ # look for ; but also allow the ending of a statement with an end
|
3826 |
|
|
# even though it is not really legal (verilog seems to accept it, so I do too)
|
3827 |
|
|
stateName => 'STMNT_JUNK_TO_SEMICOLON' ,
|
3828 |
|
|
allowAnything => 1,
|
3829 |
|
|
search => [
|
3830 |
|
|
{ regexp => ';' , },
|
3831 |
|
|
# popup and reset pos to before the end/join cope with nosemicolon case
|
3832 |
|
|
{ regexp => '\b(?:end|join|endtask|endfunction)\b' , resetPos => 1, },
|
3833 |
|
|
@$vid_vnum_or_string,
|
3834 |
|
|
],
|
3835 |
|
|
},
|
3836 |
|
|
{
|
3837 |
|
|
stateName => 'STMNT_SEMICOLON',
|
3838 |
|
|
search => [ { regexp => ';' , },
|
3839 |
|
|
# popup and reset pos to before the end/join cope with nosemicolon case
|
3840 |
|
|
{ regexp => '\b(?:end|join|endtask|endfunction)\b' , resetPos => 1, },
|
3841 |
|
|
],
|
3842 |
|
|
},
|
3843 |
|
|
{ stateName => 'BRACKET', search => [ { regexp => '\(' , },] },
|
3844 |
|
|
{ stateName => 'SEMICOLON', search => [ { regexp => ';' , },] },
|
3845 |
|
|
# V2001
|
3846 |
|
|
{
|
3847 |
|
|
stateName => 'CONFIG',
|
3848 |
|
|
allowAnything => 1,
|
3849 |
|
|
search => [ { regexp => '\bendconfig\b' , nextState => ['START'] ,},
|
3850 |
|
|
@$vid_vnum_or_string,],
|
3851 |
|
|
},
|
3852 |
|
|
{
|
3853 |
|
|
stateName => 'LIBRARY' , # just look for ;
|
3854 |
|
|
allowAnything => 1,
|
3855 |
|
|
search => [ { regexp => ';' , nextState => ['START'] , },
|
3856 |
|
|
@$vid_vnum_or_string,],
|
3857 |
|
|
},
|
3858 |
|
|
{
|
3859 |
|
|
stateName => 'GENERATE',
|
3860 |
|
|
allowAnything => 1,
|
3861 |
|
|
search => [ { regexp => '\bendgenerate\b' , nextState => ['MODULE'] ,},
|
3862 |
|
|
@$vid_vnum_or_string,],
|
3863 |
|
|
},
|
3864 |
|
|
|
3865 |
|
|
|
3866 |
|
|
|
3867 |
|
|
{ # V2001 ansi module ports
|
3868 |
|
|
stateName => 'ANSI_PORTS_TYPE',
|
3869 |
|
|
failNextState => ['ANSI_PORTS_TYPE2'],
|
3870 |
|
|
search => [ { arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b',
|
3871 |
|
|
nextState => ['ANSI_PORTS_TYPE2'],},
|
3872 |
|
|
# a null list. note this is only possible for a task or function
|
3873 |
|
|
# (a null module port list can't look like an ansi port list)
|
3874 |
|
|
# but it is not legal acording to the BNF. I allow it any way.
|
3875 |
|
|
{ regexp => '\)', nextState => ['SEMICOLON'], },
|
3876 |
|
|
],
|
3877 |
|
|
},
|
3878 |
|
|
{ # V2001 ansi module ports
|
3879 |
|
|
stateName => 'ANSI_PORTS_TYPE2',
|
3880 |
|
|
failNextState => ['ANSI_PORTS_SIGNAL_RANGE'],
|
3881 |
|
|
search => [ { arcName => 'TYPE' , regexp => "$verilog_sigs_regexp",
|
3882 |
|
|
nextState => ['ANSI_PORTS_TYPE2'],},
|
3883 |
|
|
{ regexp => '\b(?:signed)\b', nextState => ['ANSI_PORTS_TYPE2'],},],
|
3884 |
|
|
},
|
3885 |
|
|
{ # V2001 ansi module ports
|
3886 |
|
|
stateName => 'ANSI_PORTS_SIGNAL_RANGE', # for signal defs
|
3887 |
|
|
failNextState => ['ANSI_PORTS_SIGNAL_NAME'],
|
3888 |
|
|
search => [ { regexp => '\[', nextState => ['IN_SIG_RANGE','ANSI_PORTS_SIGNAL_NAME'],
|
3889 |
|
|
storePos => 1,}, ],
|
3890 |
|
|
},
|
3891 |
|
|
{ # V2001 ansi module ports
|
3892 |
|
|
stateName => 'ANSI_PORTS_SIGNAL_NAME',
|
3893 |
|
|
search => [
|
3894 |
|
|
{ arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b',
|
3895 |
|
|
nextState => ['ANSI_PORTS_TYPE'], resetPos => 1, },
|
3896 |
|
|
{ arcName => 'VID' , regexp => '$VID',
|
3897 |
|
|
nextState => ['ANSI_PORTS_SIGNAL_AFTER_NAME'], },
|
3898 |
|
|
],
|
3899 |
|
|
},
|
3900 |
|
|
{ # V2001 ansi module ports
|
3901 |
|
|
stateName => 'ANSI_PORTS_SIGNAL_AFTER_NAME',
|
3902 |
|
|
search =>
|
3903 |
|
|
[
|
3904 |
|
|
{ regexp => ',', nextState => ['ANSI_PORTS_SIGNAL_NAME'],},
|
3905 |
|
|
{ regexp => '\[', nextState => ['IN_MEM_RANGE','ANSI_PORTS_SIGNAL_AFTER_NAME'],}, # memories
|
3906 |
|
|
{ regexp => '\)', nextState => ['SEMICOLON'], } # semicolon, then pop up
|
3907 |
|
|
],
|
3908 |
|
|
},
|
3909 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3910 |
|
|
stateName => 'PPL_BRACKET' ,
|
3911 |
|
|
search => [ { regexp => '\(', nextState => ['PPL_PARAM'], }, ],
|
3912 |
|
|
},
|
3913 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3914 |
|
|
stateName => 'PPL_PARAM' ,
|
3915 |
|
|
search => [ { arcName=>'PARAM', regexp=>'\bparameter\b', nextState => ['PPL_TYPE'],},],
|
3916 |
|
|
},
|
3917 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3918 |
|
|
stateName => 'PPL_TYPE',
|
3919 |
|
|
failNextState => ['PPL_NAME'],
|
3920 |
|
|
search => [
|
3921 |
|
|
{ arcName => 'RANGE', regexp => '\[' ,
|
3922 |
|
|
nextState => ['IN_RANGE','PPL_NAME'] , },
|
3923 |
|
|
{ arcName => 'SIGNED', regexp => '\bsigned\b' ,
|
3924 |
|
|
nextState => ['PPL_TYPE'] , }, # may be followed by a range
|
3925 |
|
|
{ arcName => 'OTHER', regexp => '\b(?:integer|real|realtime|time)\b' ,
|
3926 |
|
|
nextState => ['PPL_NAME'] , },
|
3927 |
|
|
],
|
3928 |
|
|
},
|
3929 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3930 |
|
|
stateName => 'PPL_NAME',
|
3931 |
|
|
search => [
|
3932 |
|
|
{ arcName => 'NAME', regexp => '$VID' ,
|
3933 |
|
|
nextState => ['PARAMETER_EQUAL','PPL_AFTER_EQUALS'] , },
|
3934 |
|
|
],
|
3935 |
|
|
},
|
3936 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3937 |
|
|
stateName => 'PPL_AFTER_EQUALS',
|
3938 |
|
|
allowAnything => 1,
|
3939 |
|
|
search =>
|
3940 |
|
|
[
|
3941 |
|
|
{ arcName => 'CONCAT', regexp => '{' ,
|
3942 |
|
|
nextState => ['IN_CONCAT','PPL_AFTER_EQUALS'] , },
|
3943 |
|
|
{ arcName => 'BRACKET', regexp => '\(' ,
|
3944 |
|
|
nextState => ['IN_BRACKET','PPL_AFTER_EQUALS'] , },
|
3945 |
|
|
{ arcName => 'COMMA', regexp => ',' ,
|
3946 |
|
|
nextState => ['PPL_PARAM_OR_NAME'] , },
|
3947 |
|
|
{ arcName => 'END', regexp => '\)' ,
|
3948 |
|
|
nextState => ['MODULE_PORTS'] , },
|
3949 |
|
|
@$vid_vnum_or_string,
|
3950 |
|
|
]
|
3951 |
|
|
},
|
3952 |
|
|
{ # v2001 module_parameter_port_list (A.1.3)
|
3953 |
|
|
stateName => 'PPL_PARAM_OR_NAME' ,
|
3954 |
|
|
failNextState => ['PPL_NAME'],
|
3955 |
|
|
search => [ { regexp => '\bparameter\b', nextState => ['PPL_TYPE'], }, ],
|
3956 |
|
|
},
|
3957 |
|
|
];
|
3958 |
|
|
}
|
3959 |
|
|
|
3960 |
|
|
|
3961 |
|
|
############################################################
|
3962 |
|
|
# make the parser, and return it as a string
|
3963 |
|
|
############################################################
|
3964 |
|
|
|
3965 |
|
|
|
3966 |
|
|
sub _make_parser {
|
3967 |
|
|
my ($evalDefs,$genDebugCode) = @_;
|
3968 |
|
|
|
3969 |
|
|
_check_data_structures($evalDefs);
|
3970 |
|
|
|
3971 |
|
|
my $perlCode; # the perl code we are making
|
3972 |
|
|
|
3973 |
|
|
my $debugPrint = $genDebugCode ? 'print "---- $ps->{curState} $file:$line (".pos($code).")\\n" if defined $ps->{curState} && defined pos($code);':'';
|
3974 |
|
|
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
3975 |
|
|
$perlCode .= <<EOF;
|
3976 |
|
|
sub _parse_line {
|
3977 |
|
|
|
3978 |
|
|
my (\$self,\$code,\$file,\$line,\$ps,\$rs) = \@_;
|
3979 |
|
|
|
3980 |
|
|
if (!exists(\$ps->{curState})){
|
3981 |
|
|
\$ps->{curState} = undef;
|
3982 |
|
|
\$ps->{prevState}= undef;
|
3983 |
|
|
\$ps->{nextStateStack}= ["START"];
|
3984 |
|
|
\$ps->{storing}= 0;
|
3985 |
|
|
\$ps->{stored}= "";
|
3986 |
|
|
\$ps->{confusedNextState}= "START";
|
3987 |
|
|
}
|
3988 |
|
|
|
3989 |
|
|
my \$storePos = -1;
|
3990 |
|
|
my \$lastPos = 0;
|
3991 |
|
|
my \$posMark;
|
3992 |
|
|
my \$fromLastPos;
|
3993 |
|
|
PARSE_LINE_LOOP: while (1) {
|
3994 |
|
|
|
3995 |
|
|
\$lastPos = pos(\$code) if (defined(pos(\$code)));
|
3996 |
|
|
|
3997 |
|
|
if ( \$code =~ m/\\G\\s*\\Z/gs ) {
|
3998 |
|
|
last PARSE_LINE_LOOP;
|
3999 |
|
|
}
|
4000 |
|
|
else {
|
4001 |
|
|
pos(\$code) = \$lastPos;
|
4002 |
|
|
}
|
4003 |
|
|
|
4004 |
|
|
\$code =~ m/\\G\\s*/gs ; # skip any whitespace
|
4005 |
|
|
|
4006 |
|
|
\$ps->{prevState} = \$ps->{curState};
|
4007 |
|
|
\$ps->{curState} = pop(\@{\$ps->{nextStateStack}}) or
|
4008 |
|
|
die "Error: No next state after \$ps->{prevState} ".
|
4009 |
|
|
"\$file line \$line :\n \$code";
|
4010 |
|
|
$debugPrint
|
4011 |
|
|
|
4012 |
|
|
goto \$ps->{curState};
|
4013 |
|
|
die \"Confused: Bad state \$ps->{curState}\";
|
4014 |
|
|
|
4015 |
|
|
CONFUSED:
|
4016 |
|
|
\$posMark = '';
|
4017 |
|
|
# make the position marker: tricky because code can contain tabs
|
4018 |
|
|
# which we want to match in the blank space before the ^
|
4019 |
|
|
\$posMark = substr(\$code,0,\$lastPos);
|
4020 |
|
|
\$posMark =~ tr/\t/ /c ; # turn anything that isn't a tab into a space
|
4021 |
|
|
\$posMark .= "^" ;
|
4022 |
|
|
if (substr(\$code,length(\$code)-1,1) ne "\\n") { \$posMark="\\n".\$posMark; }
|
4023 |
|
|
\$self->_add_confused("\$file:\$line: in state \$ps->{prevState}:\\n".
|
4024 |
|
|
"\$code".\$posMark);
|
4025 |
|
|
\@{\$ps->{nextStateStack}} = (\$ps->{confusedNextState});
|
4026 |
|
|
return; # ignore the rest of the line
|
4027 |
|
|
EOF
|
4028 |
|
|
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
4029 |
|
|
|
4030 |
|
|
|
4031 |
|
|
foreach my $state (@$languageDef) {
|
4032 |
|
|
my $stateName = $state->{stateName};
|
4033 |
|
|
my $allowAnything = exists($state->{allowAnything}) && $state->{allowAnything};
|
4034 |
|
|
my $re = $allowAnything ? '' : '\G'; # allowAnything==0 forces a match
|
4035 |
|
|
# where we left off last time
|
4036 |
|
|
$perlCode.= " $stateName:\n";
|
4037 |
|
|
|
4038 |
|
|
if (exists($state->{confusedNextState})) {
|
4039 |
|
|
$perlCode.= " \$ps->{confusedNextState}=\"$state->{confusedNextState}\";\n";
|
4040 |
|
|
}
|
4041 |
|
|
|
4042 |
|
|
if (exists($state->{search})) {
|
4043 |
|
|
my @searchTerms=();
|
4044 |
|
|
foreach my $search (@{$state->{search}}) {
|
4045 |
|
|
push @searchTerms, $search->{regexp};
|
4046 |
|
|
}
|
4047 |
|
|
$re .= "(?:(". join(")|(",@searchTerms)."))";
|
4048 |
|
|
|
4049 |
|
|
my $failNextState='';
|
4050 |
|
|
|
4051 |
|
|
if (exists($state->{failNextState})) {
|
4052 |
|
|
if (scalar(@{$state->{failNextState}}) != 0) {
|
4053 |
|
|
$failNextState="\"".
|
4054 |
|
|
join('","',reverse(@{$state->{failNextState}})).
|
4055 |
|
|
"\"";
|
4056 |
|
|
}
|
4057 |
|
|
# else leave it set at nothing - means just popup
|
4058 |
|
|
}
|
4059 |
|
|
else {
|
4060 |
|
|
$failNextState='"CONFUSED"';
|
4061 |
|
|
}
|
4062 |
|
|
$perlCode.= " if (\$code =~ m/$re/gos) {\n";
|
4063 |
|
|
|
4064 |
|
|
my $elsif2="if";
|
4065 |
|
|
my $i=0;
|
4066 |
|
|
foreach my $search (@{$state->{search}}) {
|
4067 |
|
|
$i++;
|
4068 |
|
|
|
4069 |
|
|
my $arcName = exists($search->{arcName}) ? $search->{arcName} : '';
|
4070 |
|
|
|
4071 |
|
|
$perlCode.= " $elsif2 (defined(\$$i)) {\n";
|
4072 |
|
|
if ($genDebugCode) {
|
4073 |
|
|
$perlCode.=" print \"---- -$arcName (\$$i)->\\n\";\n";
|
4074 |
|
|
$perlCode.=" \$takenArcs->{'$stateName'}{$i}++;\n";
|
4075 |
|
|
}
|
4076 |
|
|
$elsif2="elsif";
|
4077 |
|
|
if (exists($search->{resetPos}) && $search->{resetPos}) {
|
4078 |
|
|
$perlCode.=" pos(\$code)=pos(\$code)-length(\$$i);\n";
|
4079 |
|
|
}
|
4080 |
|
|
if (exists($search->{arcName})) {
|
4081 |
|
|
$perlCode.= # " " .
|
4082 |
|
|
_make_eval_code($evalDefs,$stateName,
|
4083 |
|
|
$search->{arcName},$i,$genDebugCode);
|
4084 |
|
|
}
|
4085 |
|
|
if (exists $search->{nextState}) {
|
4086 |
|
|
$perlCode.= " push (\@{\$ps->{nextStateStack}}, \"".
|
4087 |
|
|
join('","',reverse(@{$search->{nextState}}))."\");\n";
|
4088 |
|
|
}
|
4089 |
|
|
if (exists($search->{storePos}) && $search->{storePos}) {
|
4090 |
|
|
$perlCode.= " \$ps->{storing} == 0 or\n";
|
4091 |
|
|
$perlCode.= " die \"Setting storing ".
|
4092 |
|
|
"flag when it is already set: $stateName:$arcName\";\n";
|
4093 |
|
|
$perlCode.= " \$storePos = pos(\$code);\n";
|
4094 |
|
|
$perlCode.= " \$ps->{storing} = 1;\n";
|
4095 |
|
|
$perlCode.= " \$ps->{stored} = '';\n";
|
4096 |
|
|
}
|
4097 |
|
|
$perlCode.= " }\n";
|
4098 |
|
|
}
|
4099 |
|
|
$perlCode.= " }\n";
|
4100 |
|
|
|
4101 |
|
|
if ($allowAnything) {
|
4102 |
|
|
$perlCode.= " else { ".
|
4103 |
|
|
"push(\@{\$ps->{nextStateStack}},\"$stateName\"); last PARSE_LINE_LOOP; }\n";
|
4104 |
|
|
}
|
4105 |
|
|
else {
|
4106 |
|
|
$perlCode.= " else {\n";
|
4107 |
|
|
if (exists($state->{failStorePos}) && $state->{failStorePos}) {
|
4108 |
|
|
$perlCode.= " \$ps->{storing} == 0 or\n";
|
4109 |
|
|
$perlCode.= " die \"Setting storing ".
|
4110 |
|
|
"flag when it is already set: $stateName:fail\";\n";
|
4111 |
|
|
#NB:uses lastPos here because there was no match, so can't
|
4112 |
|
|
# use pos(code)
|
4113 |
|
|
$perlCode.= " \$storePos = \$lastPos;\n";
|
4114 |
|
|
$perlCode.= " \$ps->{storing} = 1;\n";
|
4115 |
|
|
$perlCode.= " \$ps->{stored} = '';\n";
|
4116 |
|
|
}
|
4117 |
|
|
if ($failNextState) {
|
4118 |
|
|
$perlCode.="push(\@{\$ps->{nextStateStack}},$failNextState);";
|
4119 |
|
|
}
|
4120 |
|
|
$perlCode.= " pos(\$code)=\$lastPos; }\n";
|
4121 |
|
|
}
|
4122 |
|
|
}
|
4123 |
|
|
$perlCode.= " next PARSE_LINE_LOOP;\n";
|
4124 |
|
|
}
|
4125 |
|
|
$perlCode.= " }\n";
|
4126 |
|
|
$perlCode.= " if (\$storePos!=-1) { \$ps->{stored}=substr(\$code,\$storePos);}\n";
|
4127 |
|
|
$perlCode.= " elsif ( \$ps->{storing} ) { \$ps->{stored} .= \$code; }\n";
|
4128 |
|
|
$perlCode.= "}\n";
|
4129 |
|
|
|
4130 |
|
|
return $perlCode;
|
4131 |
|
|
}
|
4132 |
|
|
|
4133 |
|
|
sub _make_eval_code {
|
4134 |
|
|
my ($evalDefs,$stateName,$arcName,$matchNo,$genDebugCode) = @_;
|
4135 |
|
|
|
4136 |
|
|
my $eval='';
|
4137 |
|
|
|
4138 |
|
|
foreach my $evalDef (@$evalDefs) {
|
4139 |
|
|
|
4140 |
|
|
if (exists($evalDef->{$stateName}{$arcName})) {
|
4141 |
|
|
if ( $evalDef->{$stateName}{$arcName} =~ m/^(\w+?):(\w+?)$/ ) {
|
4142 |
|
|
$eval.=$evalDef->{$1}{$2};
|
4143 |
|
|
}
|
4144 |
|
|
else {
|
4145 |
|
|
$eval.=$evalDef->{$stateName}{$arcName};
|
4146 |
|
|
}
|
4147 |
|
|
$eval.="\n";
|
4148 |
|
|
}
|
4149 |
|
|
}
|
4150 |
|
|
# replace $match variable with the actual number of the match
|
4151 |
|
|
$eval=~ s/\$match/\$$matchNo/g;
|
4152 |
|
|
|
4153 |
|
|
# if fromLastPos is used then generate the code to work it out
|
4154 |
|
|
if ($eval =~ /\$fromLastPos/) {
|
4155 |
|
|
my $e;
|
4156 |
|
|
$e .= "\$ps->{storing}==1 or die \"fromLastPos used and storing was not set\";\n";
|
4157 |
|
|
$e .= "if (\$storePos==-1) {\n"; # on another line
|
4158 |
|
|
$e .= " \$fromLastPos=\$ps->{stored}."; # what was before
|
4159 |
|
|
$e .= " substr(\$code,0,pos(\$code)-length(\$$matchNo));\n"; # some of this line
|
4160 |
|
|
$e .= "}\n";
|
4161 |
|
|
$e .= "else {\n";
|
4162 |
|
|
$e .= " \$fromLastPos=substr(\$code,\$storePos,pos(\$code)".
|
4163 |
|
|
"-\$storePos-length(\$$matchNo));\n";
|
4164 |
|
|
$e .= "}\n";
|
4165 |
|
|
$e .= "\$ps->{storing}=0;\n";
|
4166 |
|
|
$e .= "\$ps->{stored}='';\n";
|
4167 |
|
|
$eval = $e . $eval;
|
4168 |
|
|
|
4169 |
|
|
}
|
4170 |
|
|
return $eval;
|
4171 |
|
|
}
|
4172 |
|
|
|
4173 |
|
|
sub _check_end_state {
|
4174 |
|
|
my ($self,$file,$line,$ps) = @_;
|
4175 |
|
|
|
4176 |
|
|
if (!exists($ps->{curState})){
|
4177 |
|
|
# parse_line was never called, file only contained comments, defines etc
|
4178 |
|
|
return;
|
4179 |
|
|
}
|
4180 |
|
|
$ps->{prevState} = $ps->{curState};
|
4181 |
|
|
$ps->{curState} = pop(@{$ps->{nextStateStack}}) or
|
4182 |
|
|
$self->_add_confused("$file:$line:".
|
4183 |
|
|
"No next state after $ps->{prevState} at EOF");
|
4184 |
|
|
|
4185 |
|
|
if ($ps->{curState} ne 'START') {
|
4186 |
|
|
$self->_add_confused("$file:$line:".
|
4187 |
|
|
" at EOF in state $ps->{curState}".
|
4188 |
|
|
(($ps->{curState} eq 'CONFUSED')?
|
4189 |
|
|
",prevState was $ps->{prevState}":""));
|
4190 |
|
|
}
|
4191 |
|
|
if (@{$ps->{nextStateStack}}) {
|
4192 |
|
|
$self->_add_confused("$file:$line:".
|
4193 |
|
|
" at EOF, state stack not empty: ".
|
4194 |
|
|
join(" ",@{$ps->{nextStateStack}}));
|
4195 |
|
|
}
|
4196 |
|
|
|
4197 |
|
|
# at the moment I don't check these:
|
4198 |
|
|
# $ps->{storing}= 0;
|
4199 |
|
|
# $ps->{stored}= "";
|
4200 |
|
|
|
4201 |
|
|
}
|
4202 |
|
|
|
4203 |
|
|
sub _check_data_structures {
|
4204 |
|
|
my ($evalDefs) = @_;
|
4205 |
|
|
|
4206 |
|
|
my %stateNames;
|
4207 |
|
|
my %statesUnused;
|
4208 |
|
|
|
4209 |
|
|
foreach my $sp (@$languageDef) {
|
4210 |
|
|
die "Not hash!" unless ref($sp) eq "HASH";
|
4211 |
|
|
if (!exists($sp->{stateName})) { die "State without name!"; }
|
4212 |
|
|
die "Duplicate state$sp->{stateName}" if exists $stateNames{$sp->{stateName}};
|
4213 |
|
|
$stateNames{$sp->{stateName}} = $sp;
|
4214 |
|
|
}
|
4215 |
|
|
|
4216 |
|
|
%statesUnused = %stateNames;
|
4217 |
|
|
# check language def first
|
4218 |
|
|
foreach my $sp (@$languageDef) {
|
4219 |
|
|
my %t = %$sp;
|
4220 |
|
|
if (!exists($sp->{search})) { die "State without search!"; }
|
4221 |
|
|
die "search $sp->{stateName} not array" unless ref($t{search}) eq "ARRAY";
|
4222 |
|
|
my %arcNames;
|
4223 |
|
|
foreach my $arc (@{$sp->{search}}) {
|
4224 |
|
|
my %a = %$arc;
|
4225 |
|
|
die "arc without regexp in $sp->{stateName}" unless exists $a{regexp};
|
4226 |
|
|
delete $a{regexp};
|
4227 |
|
|
if (exists($a{nextState})) {
|
4228 |
|
|
die "nextState not array" unless ref($a{nextState}) eq "ARRAY";
|
4229 |
|
|
foreach my $n (@{$a{nextState}}) {
|
4230 |
|
|
next if ($n =~ m/^\$/); #can't check variable ones
|
4231 |
|
|
die "Bad Next state $n"
|
4232 |
|
|
unless exists $stateNames{$n};
|
4233 |
|
|
delete($statesUnused{$n}) if exists $statesUnused{$n};
|
4234 |
|
|
}
|
4235 |
|
|
delete $a{nextState};
|
4236 |
|
|
}
|
4237 |
|
|
if (exists($a{arcName})) {
|
4238 |
|
|
die "Duplicate arc $a{arcName}" if exists $arcNames{$a{arcName}};
|
4239 |
|
|
$arcNames{$a{arcName}} = 1;
|
4240 |
|
|
delete $a{arcName};
|
4241 |
|
|
}
|
4242 |
|
|
delete $a{resetPos};
|
4243 |
|
|
delete $a{storePos};
|
4244 |
|
|
foreach my $k (sort (keys %a)) {
|
4245 |
|
|
die "Bad key $k in arc of state $t{stateName}";
|
4246 |
|
|
}
|
4247 |
|
|
}
|
4248 |
|
|
delete $t{stateName};
|
4249 |
|
|
delete $t{search};
|
4250 |
|
|
delete $t{allowAnything} if exists $t{allowAnything};
|
4251 |
|
|
|
4252 |
|
|
if (exists($t{confusedNextState})) {
|
4253 |
|
|
die "Bad Next confused state $t{confusedNextState}"
|
4254 |
|
|
unless exists $stateNames{$t{confusedNextState}};
|
4255 |
|
|
delete $t{confusedNextState};
|
4256 |
|
|
}
|
4257 |
|
|
|
4258 |
|
|
foreach my $n (@{$t{failNextState}}) {
|
4259 |
|
|
next if ($n =~ m/^\$/); #can't check variable ones
|
4260 |
|
|
die "Bad Next fail state $n"
|
4261 |
|
|
unless exists $stateNames{$n};
|
4262 |
|
|
delete($statesUnused{$n}) if exists $statesUnused{$n};
|
4263 |
|
|
}
|
4264 |
|
|
delete $t{failNextState} if exists $t{failNextState};
|
4265 |
|
|
delete $t{failStorePos} if exists $t{failStorePos};
|
4266 |
|
|
foreach my $k (sort (keys %t)) {
|
4267 |
|
|
die "Bad key $k in languageDef state $sp->{stateName}";
|
4268 |
|
|
}
|
4269 |
|
|
}
|
4270 |
|
|
|
4271 |
|
|
# REVISIT: MODULE PORTS looks like it is unused because it is got to
|
4272 |
|
|
# by setting $nState - should have a flag in language def that turns
|
4273 |
|
|
# off this check on a per state basis.
|
4274 |
|
|
foreach my $state (sort (keys %statesUnused)) {
|
4275 |
|
|
#die "State $state was not used";
|
4276 |
|
|
print "Warning: State $state looks like it was not used\n" if $debug;
|
4277 |
|
|
}
|
4278 |
|
|
|
4279 |
|
|
foreach my $evalDef (@$evalDefs) {
|
4280 |
|
|
foreach my $state (sort (keys %$evalDef)) {
|
4281 |
|
|
if (!exists($stateNames{$state})) {
|
4282 |
|
|
die "Couldn't find state $state";
|
4283 |
|
|
}
|
4284 |
|
|
my $statep = $stateNames{$state};
|
4285 |
|
|
|
4286 |
|
|
foreach my $arc (sort (keys %{$evalDef->{$state}})) {
|
4287 |
|
|
my $found = 0;
|
4288 |
|
|
foreach my $s (@{$statep->{search}}) {
|
4289 |
|
|
if (exists($s->{arcName}) && ($s->{arcName} eq $arc)) {
|
4290 |
|
|
$found=1;
|
4291 |
|
|
last;
|
4292 |
|
|
}
|
4293 |
|
|
}
|
4294 |
|
|
if ($found == 0) {
|
4295 |
|
|
die "No arc $arc in state $state";
|
4296 |
|
|
}
|
4297 |
|
|
if ( $evalDef->{$state}{$arc} =~ m/^(\w+?):(\w+?)$/ ) {
|
4298 |
|
|
die "No code found for $evalDef->{$state}{$arc}"
|
4299 |
|
|
unless exists $evalDef->{$1}{$2};
|
4300 |
|
|
}
|
4301 |
|
|
}
|
4302 |
|
|
}
|
4303 |
|
|
}
|
4304 |
|
|
}
|
4305 |
|
|
|
4306 |
|
|
|
4307 |
|
|
sub _check_coverage {
|
4308 |
|
|
|
4309 |
|
|
print "\n\nCoverage Information:\n";
|
4310 |
|
|
foreach my $sp (@$languageDef) {
|
4311 |
|
|
if (!exists($takenArcs->{$sp->{stateName}})) {
|
4312 |
|
|
print " State $sp->{stateName}: no arcs take (except fail maybe)\n";
|
4313 |
|
|
}
|
4314 |
|
|
else {
|
4315 |
|
|
my $i=0;
|
4316 |
|
|
foreach my $arc (@{$sp->{search}}) {
|
4317 |
|
|
$i++;
|
4318 |
|
|
if (!exists( $takenArcs->{$sp->{stateName}}{$i} )) {
|
4319 |
|
|
my $arcName = $i;
|
4320 |
|
|
$arcName = $arc->{arcName} if exists $arc->{arcName};
|
4321 |
|
|
print " Arc $arcName of $sp->{stateName} was never taken\n";
|
4322 |
|
|
}
|
4323 |
|
|
}
|
4324 |
|
|
}
|
4325 |
|
|
}
|
4326 |
|
|
}
|
4327 |
|
|
|
4328 |
|
|
|
4329 |
|
|
###########################################################################
|
4330 |
|
|
|
4331 |
|
|
# when doing require or use we must return 1
|
4332 |
|
|
1;
|
4333 |
|
|
|