File Coverage

blib/lib/Perlilog/sysclasses/PLglobal.pl
Criterion Covered Total %
statement 5 162 3.0
branch 1 92 1.0
condition 0 21 0.0
subroutine 1 7 14.2
pod n/a
total 7 282 2.4


line stmt bran cond sub pod time code
1             #
2             # This file is part of the Perlilog project.
3             #
4             # Copyright (C) 2003, Eli Billauer
5             #
6             # This program is free software; you can redistribute it and/or modify
7             # it under the terms of the GNU General Public License as published by
8             # the Free Software Foundation; either version 2 of the License, or
9             # (at your option) any later version.
10             #
11             # This program is distributed in the hope that it will be useful,
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14             # GNU General Public License for more details.
15             #
16             # You should have received a copy of the GNU General Public License
17             # along with this program; if not, write to the Free Software
18             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19             #
20             # A copy of the license can be found in a file named "licence.txt", at the
21             # root directory of this project.
22             #
23              
24             ${__PACKAGE__.'::errorcrawl'}='system';
25             sub who {
26 0     0   0 return "The Global Object";
27             }
28              
29             sub new {
30 1     1   1 my $this = shift;
31 1         6 my $self = $this->SUPER::new(@_);
32              
33 1         3 my $name = $self->get('name');
34 1 50       3 puke("The \'global\' class can generate an object only with the name \'globalobject\'".
35             " and not \'$name\'\n") unless ($name eq 'globalobject');
36              
37 1         2 return $self;
38             }
39              
40             sub complete {
41 0     0     my $self = shift;
42 0           my $dir=$self->get('filesdir');
43 0 0         blow("The \'filesdir\' property was not set for ".$self->who()."\n")
44             unless ($dir);
45 0 0         mkdir $dir, 0777 unless -e $dir;
46 0 0         opendir(DIR,$dir) || blow("Failed to open $dir as a directory\n");
47 0           my @A=readdir(DIR);
48 0           closedir(DIR);
49 0           foreach (grep /[^.]/, @A) {
50 0           unlink "$dir/$_";
51             }
52             }
53              
54             # NOTE: execute does not allow extra methods or objects to be
55             # added once started.
56              
57             sub execute {
58 0     0     my $global = shift; # We're the global object, aren't we?
59 0 0         puke("The execute method was not run from the global object\n")
60             unless ($global == $global->globalobj());
61 0           my $system = $global -> get('system');
62 0           my @methods = $system -> get('methods');
63 0           my @objects = ($global -> get('beginobjects'),
64             $global -> get('objects'),
65             $global -> get('endobjects'));
66 0           my ($method, $object);
67              
68             # Note that the global object sneaks in first here
69 0           @methods = grep { defined } @methods;
  0            
70 0           @objects = grep { defined } ($global, @objects);
  0            
71              
72 0           foreach $method (@methods) {
73 0           foreach $object (@objects) {
74 0           $object->$method();
75             }
76 0 0         last if ($Perlilog::wrongflag);
77             }
78             }
79              
80             sub constreset {
81 0     0     my ($self, $ID, $type) = @_;
82             wrong ("Reset of unknown type \'$type\'")
83 0 0         unless grep {$type eq $_} qw(sync negsync async negasync);
  0            
84 0 0         wrong ("Unproper ID \'$ID\' given for reset signal\n")
85             unless (defined $Perlilog::VARS[$ID]);
86             # $self is global object!
87 0           $self->const('reset_type', $type);
88 0           $self->const('reset_ID', $ID);
89             }
90              
91             sub instantiate {
92 0     0     my $self = shift;
93 0           $self->SUPER::instantiate(@_);
94 0           my ($i, $ID, $drive, $obj, $var, $type, $parent);
95 0           my ($from, $start, $to, $next, $f, $t, $toname);
96 0           my ($fv, $tv, $dim, $nv, $nID, $tmp, $wf, $hashref);
97              
98 0           my %eqvars;
99 0           my @eq;
100              
101             # Type conversion hashes
102 0           my %toin=('input' => 'input',
103             'wire' => 'input',
104             'inout' => 'inout',
105             'output'=> 'inout');
106 0           my %toout=('output' => 'output',
107             'reg' => 'outreg',
108             'outreg' => 'outreg',
109             'wire' => 'output',
110             'inout' => 'inout',
111             'input' => 'inout');
112              
113             # We begin with triggering off tree studies.
114 0           foreach $i (values %Perlilog::objects) {
115 0 0         next unless (defined $i->get('inshash')); # Only Verilog objects...
116 0 0         next if (ref $i->get('parent')); # Only "root" objects...
117 0           $i->treestudy;
118             }
119              
120             # Now we collapse the EQVARS list to the minimal number
121             # of distinct lists. Note that the hash keys are the
122             # string representation of the reference, and only
123             # functions as a unique representation of the reference.
124             # The value points to the index in EQVARS, which makes
125             # is possible to retrieve the EQVARS list again.
126             # We loop in reverse order, so that the value will represent
127             # the variable in the cluster that was defined earliest.
128              
129 0           my $imax = $#Perlilog::EQVARS;
130 0           for ($i=$imax; $i>=0; $i--) {
131 0 0         next unless (ref $Perlilog::EQVARS[$i]);
132 0           $eqvars{$Perlilog::EQVARS[$i]}=$i;
133             }
134              
135 0           my @in;
136             my @out;
137 0           my @zout;
138 0           my %where;
139              
140             # This little subroutine will help up make nice error messages.
141             # Note that it runs in the current scope.
142              
143             my $s = sub {
144 0     0     my $r = "These are the variables involved:\n";
145 0 0         if (@out) {
146 0           $r.="Driving variables:\n";
147 0           foreach (@out)
148 0           { $r.=" Variable ".$self->varwho($_)."\n"; }
149             }
150 0 0         if (@zout) {
151 0           $r.="Weakly driving variables:\n";
152 0           foreach (@zout)
153 0           { $r.=" Variable ".$self->varwho($_)."\n"; }
154             }
155 0 0         if (@in) {
156 0           $r.="Driven variables:\n";
157 0           foreach (@in)
158 0           { $r.=" Variable ".$self->varwho($_)."\n"; }
159             }
160 0           return $r;
161 0           };
162              
163             # This is the main loop. Each $i is a variable cluster that
164             # needs to be interconnected.
165              
166 0           foreach $i (sort values %eqvars) {
167              
168 0           my @ids=@{$Perlilog::EQVARS[$i]}; # Get a local copy. The original may change
  0            
169 0 0         next unless ($#ids>0); # No hassle with unconnected variables
170              
171 0           @in=(); @out=(); @zout=();
  0            
  0            
172 0           %where=();
173              
174             # We now distribute the variables to the respective lists. We
175             # also set up the %where hash that tells us the names of the
176             # variables in the objects, if they exist. Again, the keys
177             # are not real references but string representations, but it's
178             # good enough for looking up.
179             IDLOOP:
180 0           foreach $ID (sort @ids) {
181 0           ($obj, $var) = @{$Perlilog::VARS[$ID]};
  0            
182 0           $drive = $obj->get(['vars', $var, 'drive']);
183              
184             # If $where{$obj} is already defined, it means we have two
185             # equal variables in the same module. This is handled quite
186             # gracefully as long as they don't happen to be both zouts.
187             # For the case when they are both zouts, by make a nonstrength-
188             # reducing transistor connecting, as would an inout connection,
189             # and don't deal with the new variable any more.
190              
191 0 0         if (defined $where{$obj}) {
192 0 0         if ($drive eq 'zout') {
193 0 0         if ($obj->get(['vars', $where{$obj}, 'drive']) eq 'zout') {
194             # Horrors! Two zouts in the same module!
195 0           my $tranins = $obj->suggestins('PL_tran');
196 0           $obj->addins($tranins, 'detached');
197             wrong("Failed to handle bidirectional variable \'".$var."\' in ".$obj->who.
198             " because the Verilog is static\n")
199 0 0         unless ($obj->append(" tran $tranins ($var, ".$where{$obj}.");\n"));
200 0           next IDLOOP; # Don't register this variable. It's already handled
201             } else {
202             # The existing variable wasn't a zout, but we'll set $where{$obj} to this
203             # variable, so we won't miss a zout clash in the future...
204 0           $where{$obj} = $var;
205             }
206             }
207             # Note that we do nothing if this is not a zout case. We let the previously
208             # registered variable persist.
209             } else {
210 0           $where{$obj} = $var; # This is just the normal case. A first-timer
211             }
212            
213             # We put the variable in the right list, according to "drive"
214              
215 0 0         if ($drive eq 'in') { push @in, $ID; }
  0 0          
    0          
    0          
216 0           elsif ($drive eq 'out') { push @out, $ID; }
217 0           elsif ($drive eq 'zout') { push @zout, $ID; }
218             elsif ($drive eq 'via') {
219 0           wrong("Variable ".$self->varwho($ID).
220             " was of drive-type \'via\' (System error?)\n");
221             } else {
222 0           wrong("Variable ".$self->varwho($ID).
223             " is of unknown drive-type \'$drive\'\n");
224             }
225             }
226              
227             # Now we complain if things aren't so good...
228 0 0 0       if (($#out<0) && ($#zout<0)) {
    0 0        
    0          
229 0           wrong("No driving variable in cluster\n".&$s);
230             } elsif ($#out>0) {
231 0           wrong("More than one exclusively driving variable in cluster\n".&$s);
232             } elsif (($#out==0) && ($#zout>=0)) {
233 0           wrong("Exclusiveness of driving variable was offended by weakly driven variables\n".&$s);
234             }
235              
236             # Now we draw lines from every driving variable to every
237             # driven variable.
238              
239             FLOOP: # The "from" loop -- driving variables
240 0           foreach $f ((sort @out), (sort @zout)) {
241 0           ($start, $fv) = @{$Perlilog::VARS[$f]};
  0            
242             TLOOP: # The "to" loop -- driven variables
243 0           foreach $t ((sort @in), (sort @zout)) {
244 0 0         next TLOOP if ($t == $f);
245 0           ($to, $tv) = @{$Perlilog::VARS[$t]};
  0            
246 0           $from = $start;
247 0           $toname = $to->get('name');
248              
249             # If we happen to start and end at the same object,
250             # why hassle? Just make an internal assignment. But
251             # alas, the current object may not allow its Verilog
252             # content to change, in which case append() fails.
253             # In that case we simply go on, which will cause
254             # a walk-up to the parent and back (good).
255             next TLOOP
256 0 0 0       if (($start == $to) &&
257             ($start->append(" assign $tv = $fv;\n")));
258              
259             # OK, now we come to SLOOP: The walking around loop.
260             # We travel our way to $to. treestudy() earlier
261             # promised to take us there, so we trust it and
262             # run the loop until we reach the place.
263              
264             SLOOP:
265 0           while (1) {
266             # We fetch the next object to walk to
267 0           $next = ${$from->get('treepath')}{$toname};
  0            
268 0 0         unless (ref $next) {
269 0           wrong("No path found between variables ".$self->varwho($f).
270             " and ".$self->varwho($t)."\n");
271 0           next TLOOP;
272             }
273            
274             # Now the world splits in two: Either we went from child
275             # to parent, or the opposite way. Anyhow, this takes
276             # opposite treatment, since we always create the inputs and
277             # outputs on the child, whereas the parent gets a "wire" at
278             # most.
279              
280 0           $parent = $next->get('parent');
281 0 0 0       if (defined ($parent) && ($parent == $from)) {
282              
283             # This is the parent to child walk part:
284              
285             # Get the variable name an $next's object. If we happen to
286             # have reached our destination, take $tv. This is because
287             # if there are two input variables in the same object,
288             # only one will be represented in $where{$next}
289              
290 0 0         $nv = ($next==$to) ? $tv : $where{$next};
291              
292             # If $nv is not defined, it means that object currently
293             # has no access to the variable. We create a via.
294 0 0         unless (defined $nv) {
295              
296             # Now we want to set the name nicely. If the current object
297             # has the 'viasource' (list) property set, we scan through the objects
298             # from which we may borrow the name. Only non-via variables
299             # may donate names.
300            
301             VIALOOP1:
302 0           foreach my $source ($next->get('viasource')) {
303 0 0 0       if ((defined $where{$source}) &&
304             ($source->get(['vars',$where{$source},'drive']) ne 'via')) {
305 0           $nv = $next->suggestvar($where{$source}); # This is a good source!
306 0           last VIALOOP1; # No more search!
307             }
308             }
309              
310 0 0         $nv = $next->suggestvar($fv.'_via') # Make _via
311             unless (defined $nv);
312              
313 0           $nID = $next->addvar($nv, 'wire', 'via');
314 0           $next->attach($f, $nID); # This will also get the 'dim' property right
315 0           $where{$next}=$nv; # Register it, so we won't do this again
316             }
317            
318             # Now we change the variable's type if needed.
319 0           $tmp = $toin{$next->get(['vars',$nv,'type'])};
320 0 0         blow("Expected a variable convertable to input/inout, got ".
321             "variable \'$nv\' of type \'".$next->get(['vars',$nv,'type'])."\' on ".
322             $next->who."\n")
323             unless (defined $tmp);
324              
325             # We can't change variable types of static objects. Be sure.
326            
327 0 0         if ($next->get('static')) {
328 0 0         wrong("Attempted to change the variable type of $nv to $tmp in ".
329             $next->who()." but it is a static Verilog object\n")
330             unless ($next->get(['vars',$nv,'type']) eq $tmp)
331             } else {
332 0           $next->set(['vars',$nv,'type'], $tmp);
333             }
334              
335             # And finally, we register the connection in 'inshash'. We are not
336             # worried about if the entry is already set, because it will always
337             # be set to the same value, $where{$from}
338              
339 0           $hashref = $next->get('inshash');
340 0           ${$hashref}{$nv}=$where{$from};
  0            
341            
342             } else {
343              
344             # This is the child to parent walk part: (quite similar)
345              
346             # Get the variable name an $next's object. If we happen to
347             # have reached our destination, take $tv. This is because
348             # if there are two input variables in the same object,
349             # only one will be represented in $where{$next}
350              
351 0 0         $nv = ($next==$to) ? $tv : $where{$next};
352              
353             # If $nv is not defined, it means that object currently
354             # has no access to the variable. We create a via.
355 0 0         unless (defined $nv) {
356              
357             # Now we want to set the name nicely. If the current object
358             # has the 'viasource' (list) property set, we scan through the objects
359             # from which we may borrow the name. Only non-via variables
360             # may donate names.
361            
362             VIALOOP2:
363 0           foreach my $source ($next->get('viasource')) {
364 0 0 0       if ((defined $where{$source}) &&
365             ($source->get(['vars',$where{$source},'drive']) ne 'via')) {
366 0           $nv = $next->suggestvar($where{$source}); # This is a good source!
367 0           last VIALOOP2; # No more search!
368             }
369             }
370              
371 0 0         $nv = $next->suggestvar($fv.'_via') # Make _via
372             unless (defined $nv);
373            
374 0           $nID = $next->addvar($nv, 'wire', 'via');
375 0           $next->attach($f, $nID); # This will also get the 'dim' property right
376 0           $where{$next}=$nv; # Register it, so we won't do this again
377             }
378            
379             # Now we change the variable's type if needed.
380 0           $wf = $where{$from}; # We use it a lot here, so...
381 0           $tmp = $toout{$from->get(['vars',$wf,'type'])};
382 0 0         blow("Expected a variable convertable to output/inout, got ".
383             "variable \'$wf\' of type \'".$from->get(['vars',$wf,'type'])."\' on ".
384             $from->who."\n")
385             unless (defined $tmp);
386              
387             # We can't change variable types of static objects. Be sure.
388              
389 0 0         if ($from->get('static')) {
390 0 0         wrong("Attempted to change the variable type of $wf to $tmp in ".
391             $from->who()." but it is a static Verilog object\n")
392             unless ($from->get(['vars',$wf,'type']) eq $tmp)
393             } else {
394 0           $from->set(['vars',$wf,'type'], $tmp);
395             }
396              
397             # And finally, we register the connection in 'inshash'. If the entry
398             # is already initialized, then we've already connected that variable.
399             # We use an assign instead. Note that this won't work with zouts.
400 0           $hashref = $from->get('inshash');
401 0           $tmp = ${$hashref}{$wf};
  0            
402 0 0 0       if ((defined $tmp) && ($tmp ne $nv)) {
403 0           $next->append(" assign $nv = $tmp;\n");
404             } else {
405 0           ${$hashref}{$wf}=$nv;
  0            
406             }
407             }
408              
409             # Now it's time to see if we're finished. That is, have we
410             # reached our destination?
411              
412 0 0         last SLOOP if ($next == $to);
413              
414 0           $from = $next; # This is the actual walking
415             }
416             }
417             }
418             }
419             }