File Coverage

blib/lib/VIC/PIC/Gpsim.pm
Criterion Covered Total %
statement 276 323 85.4
branch 115 210 54.7
condition 21 51 41.1
subroutine 26 26 100.0
pod 0 14 0.0
total 438 624 70.1


line stmt bran cond sub pod time code
1             package VIC::PIC::Gpsim;
2 34     34   122 use strict;
  34         46  
  34         792  
3 34     34   568 use warnings;
  34         47  
  34         631  
4 34     34   539 use bigint;
  34         2140  
  34         187  
5 34     34   52078 use Carp;
  34         48  
  34         1448  
6 34     34   728 use Pegex::Base; # use this instead of Mo
  34         1689  
  34         177  
7              
8             our $VERSION = '0.31';
9             $VERSION = eval $VERSION;
10              
11             has type => 'gpsim';
12              
13             has include => 'coff.inc';
14              
15             has pic => undef; # refer to the PIC object
16              
17             has node_count => 0;
18              
19             has scope_channels => 0;
20              
21             has stimulus_count => 0;
22              
23             has should_autorun => 0;
24              
25             has disable => 0;
26              
27             sub supports_modifier {
28 12     12 0 139 my $self = shift;
29 12         26 my $mod = shift;
30 12 100       115 return 1 if $mod =~ /^(?:every|wave)$/i;
31 1         5 0;
32             }
33              
34             sub init_code {
35 19     19 0 85 my $self = shift;
36 19 50       58 croak "This chip is not supported" unless $self->pic->doesroles(qw(Chip CodeGen GPIO));
37 19         37 my $pic = '';
38 19 50       54 $pic = $self->pic->type if $self->pic;
39 19 50       213 my $freq = $self->pic->f_osc if $self->pic;
40 19 50       161 if ($freq) {
41 19         57 $freq = qq{\t.sim "$pic.frequency = $freq"};
42             } else {
43 0         0 $freq = '';
44             }
45 19         75 return << "...";
46             ;;;; generated common code for the Simulator
47             \t.sim "module library libgpsim_modules"
48             \t.sim "$pic.xpos = 200"
49             \t.sim "$pic.ypos = 200"
50             $freq
51             ...
52             }
53              
54             sub _gen_led {
55 30     30   33 my $self = shift;
56 30         43 my ($id, $x, $y, $name, $port, $color) = @_;
57 30 100 66     109 if (defined $color and ref $color eq 'HASH') {
58 4         6 $color = $color->{string};
59             }
60 30 100       61 $color = 'red' unless defined $color;
61 30 50       156 $color = 'red' unless $color =~ /red|orange|green|yellow|blue/i;
62 30         39 $color = lc $color;
63 30 50       66 $color = substr ($color, 1) if $color =~ /^@/;
64 30         77 return << "...";
65             \t.sim "module load led L$id"
66             \t.sim "L$id.xpos = $x"
67             \t.sim "L$id.ypos = $y"
68             \t.sim "L$id.color = $color"
69             \t.sim "node $name"
70             \t.sim "attach $name $port L$id.in"
71             ...
72             }
73              
74              
75             sub _get_gpio_info {
76 101     101   91 my ($self, $port) = @_;
77 101         149 my $gpio_pin = $self->pic->get_input_pin($port);
78 101 50       144 if ($gpio_pin) {
79             # this is a pin
80 101         72 return @{$self->pic->input_pins->{$gpio_pin}};
  101         151  
81             } else {
82 0         0 $gpio_pin = $self->pic->get_output_pin($port);
83 0 0       0 if ($gpio_pin) {
84             # this is a pin
85 0         0 return @{$self->pic->output_pins->{$gpio_pin}};
  0         0  
86             }
87             }
88 0         0 return;
89             }
90              
91             sub _get_simreg {
92 32     32   46 my ($self, $port) = @_;
93 32         43 my $simreg = lc $port;
94 32 50       56 if ($self->pic) {
95 32 100       136 if (exists $self->pic->registers->{$port}) {
    50          
96             # this is a port
97 1         9 $simreg = lc $port;
98             } elsif (exists $self->pic->pins->{$port}) {
99 31         252 my ($io1) = $self->_get_gpio_info($port);
100 31 50       150 if (defined $io1) {
101 31         48 $simreg = lc $io1;
102             } else {
103 0         0 my $pic = $self->pic->type;
104 0         0 carp "Cannot find '$port' in PIC $pic. Using '$simreg'";
105             }
106             } else {
107 0         0 my $pic = $self->pic->type;
108 0         0 carp "Cannot find '$port' in PIC $pic. Using '$simreg'";
109             }
110             }
111 32         48 return $simreg;
112             }
113              
114             sub _get_simport {
115 94     94   105 my ($self, $port, $pin) = @_;
116 94         133 my $simport = lc $port;
117 94 50       186 if ($self->pic) {
118 94 100       467 if (exists $self->pic->registers->{$port}) {
    50          
119             # this is a port
120 25         122 $simport = lc $port;
121 25 100       69 $simport .= $pin if defined $pin;
122             } elsif (exists $self->pic->pins->{$port}) {
123 69         513 my ($io1, $io2, $io3) = $self->_get_gpio_info($port);
124 69 50 33     472 if (defined $io1 and defined $io3) {
125 69         122 $simport = lc "$io1$io3";
126             } else {
127 0         0 my $pic = $self->pic->type;
128 0         0 carp "Cannot find '$port' in PIC $pic. Using '$simport'";
129             }
130             } else {
131 0         0 my $pic = $self->pic->type;
132 0         0 carp "Cannot find '$port' in PIC $pic. Using '$simport'";
133             }
134             }
135 94         139 return $simport;
136             }
137              
138             sub _get_portpin {
139 27     27   26 my ($self, $port) = @_;
140 27         30 my $simport = lc $port;
141 27         21 my $simpin;
142 27 50       58 if ($self->pic) {
143 27 50       110 if (exists $self->pic->registers->{$port}) {
    100          
144             # this is a port
145 0         0 $simport = lc $port;
146             } elsif (exists $self->pic->pins->{$port}) {
147 1         12 my ($io1, $io2, $io3) = $self->_get_gpio_info($port);
148 1 50       7 if (defined $io1) {
149 1         1 $simport = lc $io1;
150 1         2 $simpin = $io3;
151             } else {
152 0         0 my $pic = $self->pic->type;
153 0         0 carp "Cannot find '$port' in PIC $pic. Using '$simport'";
154             }
155             } else {
156 26         195 return;
157             }
158             }
159 1 50       3 return wantarray ? ($simport, $simpin) : $simport;
160             }
161              
162             sub attach_led {
163 17     17 0 107 my ($self, $port, $count, $color) = @_;
164 17 100       48 $count = 1 unless $count;
165 17 50       55 $count = 1 if int($count) < 1;
166 17         742 my $code = '';
167 17 100       35 if ($count == 1) {
168 14         242 my $c = $self->node_count;
169 14         55 my $node = lc $port . 'led';
170 14         36 $self->node_count($c + 1);
171 14 50       770 my $x = ($c >= 4) ? 400 : 100;
172 14         273 my $y = 50 + 50 * $c;
173             # use the default pin 0 here
174 14         1157 my $simport = $self->_get_simport($port, 0);
175 14         44 $code = $self->_gen_led($c, $x, $y, $node, $simport, $color);
176             } else {
177 3         188 $count--;
178 3 50       17 if ($self->pic) {
179 3         20 for (0 .. $count) {
180 16         1138 my $c = $self->node_count + $_;
181 16 100       1516 my $x = ($_ >= 4) ? 400 : 100;
182 16         1028 my $y = 50 + 50 * $c;
183 16         1432 my $node = lc $port . $c . 'led';
184 16         288 my $simport = $self->_get_simport($port, $_);
185 16         33 $code .= $self->_gen_led($c, $x, $y, $node, $simport, $color);
186             }
187 3         251 $self->node_count($self->node_count + $count + 1);
188             }
189             }
190 17         1568 return $code;
191             }
192              
193             sub attach_led7seg {
194 1     1 0 6 my ($self, @pins) = @_;
195 1         1 my $code = '';
196 1         2 my @simpins = ();
197 1         1 my $color = 'red';
198 1         2 foreach my $p (@pins) {
199 2 50 33     9 if (defined $p and ref $p eq 'HASH') {
200 0         0 $p = $p->{string};
201 0 0       0 next unless defined $p;
202             }
203 2 100       5 if (exists $self->pic->pins->{$p}) {
    50          
    0          
204 1         6 push @simpins, $p;
205             } elsif (exists $self->pic->registers->{$p}) {
206             # find all the output pins for the port
207 1         7 foreach (sort(keys %{$self->pic->output_pins})) {
  1         2  
208 17 50       162 next unless defined $self->pic->output_pins->{$_}->[0];
209 17 100       166 push @simpins, $_ if $self->pic->output_pins->{$_}->[0] eq $p;
210             }
211             } elsif ($p =~ /red|orange|green|yellow|blue/i) {
212 0         0 $color = $p;
213 0 0       0 $color = substr($p, 1) if $p =~ /^@/;
214 0         0 next;
215             } else {
216 0         0 carp "Ignoring port $p as it doesn't exist\n";
217             }
218             }
219 1 50       13 return unless scalar @simpins;
220 1         3 my $id = $self->node_count;
221 1         5 $self->node_count($id + 1);
222 1         47 my $x = 500;
223 1         2 my $y = 50 + 50 * $id;
224 1         73 $code .= << "...";
225             \t.sim "module load led_7segments L$id"
226             \t.sim "L$id.xpos = $x"
227             \t.sim "L$id.ypos = $y"
228             ...
229 1         56 my @nodes = qw(cc seg0 seg1 seg2 seg3 seg4 seg5 seg6);
230 1         3 foreach my $n (@nodes) {
231 8         90 my $p = shift @simpins;
232 8         11 my $sp = $self->_get_simport($p);
233 8         18 $code .= << "...";
234             \t.sim "node $n"
235             \t.sim "attach $n $sp L$id.$n"
236             ...
237             }
238 1         15 return $code;
239             }
240              
241             sub stop_after {
242 16     16 0 88 my ($self, $usecs) = @_;
243             # convert $secs to cycles
244 16         52 my $cycles = $usecs * 10;
245 16         1469 my $code = << "...";
246             \t.sim "break c $cycles"
247             ...
248 16         279 return $code;
249             }
250              
251             sub logfile {
252 7     7 0 38 my ($self, $file) = @_;
253 7 50       19 $file = "vicsim.log" unless defined $file;
254 7 50       19 if (ref $file eq 'HASH') {
255 7   50     25 $file = $file->{string} || 'vicsim.log';
256             }
257 7 50       24 $file = substr($file, 1) if $file =~ /^@/;
258 7 50       45 return "\t.sim \"log lxt $file\"\n" if $file =~ /\.lxt/i;
259 0         0 return "\t.sim \"log on $file\"\n";
260             }
261              
262             sub log {
263 20     20 0 92 my $self = shift;
264 20         32 my $code = '';
265 20         40 foreach my $port (@_) {
266 29 100       73 if ($port =~ /US?ART/) {
267 3 50       11 next unless $self->pic->doesrole('USART');
268 3         8 my $ipin = $self->pic->usart_pins->{async_in};
269 3         15 my $opin = $self->pic->usart_pins->{async_out};
270 3 50 33     25 if (defined $ipin and defined $opin) {
271 3         11 my $ireg = $self->_get_simreg($ipin);
272 3         6 my $oreg = $self->_get_simreg($opin);
273 3         17 $code .= $self->log($ipin);
274 3 50       15 $code .= $self->log($opin) if $ireg ne $oreg;
275             }
276             } else {
277 26         62 my $reg = $self->_get_simreg($port);
278 26 50       53 next unless $reg;
279 26         79 $code .= << "...";
280             \t.sim "log r $reg"
281             \t.sim "log w $reg"
282             ...
283             }
284             }
285 20         65 return $code;
286             }
287              
288             sub _set_scope {
289 33     33   34 my ($self, $port) = @_;
290 33         65 my $simport = $self->_get_simport($port);
291 33         92 my $chnl = $self->scope_channels;
292 33 50       128 carp "Maximum of 8 channels can be used in the scope\n" if $chnl > 7;
293 33 50       636 return '' if $chnl > 7;
294 33 100       506 if (lc($simport) eq lc($port)) {
295 1         3 my @code = ();
296 1         3 for (0 .. 7) {
297 8         204 $simport = $self->_get_simport($port, $_);
298 8 50       20 if ($self->scope_channels < 8) {
299 8         205 $chnl = $self->scope_channels;
300 8         47 push @code, "\t.sim \"scope.ch$chnl = \\\"$simport\\\"\"";
301 8         170 $self->scope_channels($chnl + 1);
302             }
303 8 50       515 carp "Maximum of 8 channels can be used in the scope\n" if $chnl > 7;
304 8 50       188 last if $chnl > 7;
305             }
306 1         26 return join("\n", @code);
307             } else {
308 32         65 $self->scope_channels($chnl + 1);
309 32         1462 return << "...";
310             \t.sim "scope.ch$chnl = \\"$simport\\""
311             ...
312             }
313             }
314              
315             sub scope {
316 17     17 0 82 my $self = shift;
317 17         35 my $code = '';
318 17         37 foreach my $port (@_) {
319 30 100       298 if ($port =~ /US?ART/) {
320 3 50       7 next unless $self->pic->doesrole('USART');
321 3         9 my $ipin = $self->pic->usart_pins->{async_in};
322 3         15 my $opin = $self->pic->usart_pins->{async_out};
323 3 50       17 $code .= $self->_set_scope($ipin) if defined $opin;
324 3 50       61 $code .= $self->_set_scope($opin) if defined $opin;
325             } else {
326 27         58 $code .= $self->_set_scope($port);
327             }
328             }
329 17         279 return $code;
330             }
331              
332             ### have to change the operator back to the form acceptable by gpsim
333             sub _get_operator {
334 27     27   23 my $self = shift;
335 27         26 my $op = shift;
336 27 50       60 return '==' if $op eq 'EQ';
337 0 0       0 return '!=' if $op eq 'NE';
338 0 0       0 return '>' if $op eq 'GT';
339 0 0       0 return '>=' if $op eq 'GE';
340 0 0       0 return '<' if $op eq 'LT';
341 0 0       0 return '<=' if $op eq 'LE';
342 0         0 return undef;
343             }
344              
345             sub sim_assert {
346 29     29 0 121 my ($self, $condition, $msg) = @_;
347 29         27 my $assert_msg;
348 29 100       65 if ($condition =~ /@@/) {
349 27         60 my @args = split /@@/, $condition;
350 27         51 my $literal = qr/^\d+$/;
351 27 50       49 if (scalar @args == 3) {
352 27         1574 my $lhs = shift @args;
353 27         28 my $op = shift @args;
354 27         25 my $rhs = shift @args;
355 27         49 my $op2 = $self->_get_operator($op);
356 27 50       133 if ($lhs !~ $literal) {
357 27         51 my ($port, $pin) = $self->_get_portpin($lhs);
358 27 100       59 if (defined $pin) {
    50          
359 1         4 my $pval = sprintf "0x%02X", (1 << $pin);
360 1         151 $lhs = lc "($port & $pval)";
361             } elsif (defined $port) {
362 0         0 $lhs = lc $port;
363             } else {
364             # may be a variable
365 26         33 $lhs = uc $lhs;
366             }
367             } else {
368 0         0 $lhs = sprintf "0x%02X", $lhs;
369             }
370 27 50       119 if ($rhs !~ $literal) {
371 0         0 my ($port, $pin) = $self->_get_portpin($lhs);
372 0 0       0 if (defined $pin) {
    0          
373 0         0 my $pval = sprintf "0x%02X", (1 << $pin);
374 0         0 $rhs = lc "($port & $pval)";
375             } elsif (defined $port) {
376 0         0 $rhs = lc $port;
377             } else {
378             # may be a variable
379 0         0 $rhs = uc $rhs;
380             }
381             } else {
382 27         94 $rhs = sprintf "0x%02X", $rhs;
383             }
384 27         48 $condition = "$lhs $op2 $rhs";
385             }
386             #TODO: handle more complex expressions
387 27 100 66     110 if (defined $msg and ref $msg eq 'HASH') {
388 26         42 $msg = $msg->{string};
389             }
390 27 100       47 $msg = "$condition is false" unless $msg;
391 27 50       48 $msg = substr($msg, 1) if $msg =~ /^@/;
392 27 50       37 $condition = substr($condition, 1) if $condition =~ /^@/;
393 27         58 $assert_msg = qq{$condition, \\\"$msg\\\"};
394             } else {
395 2 50 33     9 if (defined $msg and ref $msg eq 'HASH') {
396 0         0 $msg = $msg->{string};
397             }
398 2 50 33     18 if (defined $condition and ref $condition eq 'HASH') {
399 2         7 $condition = $condition->{string};
400             }
401 2 50 33     37 if (defined $condition and defined $msg) {
    50 33        
    0 0        
402 0 0       0 $msg = substr($msg, 1) if $msg =~ /^@/;
403 0 0       0 $condition = substr($condition, 1) if $condition =~ /^@/;
404 0         0 $assert_msg = qq{$condition, \\\"$msg\\\"};
405             } elsif (defined $condition and not defined $msg) {
406 2 50       8 $condition = substr($condition, 1) if $condition =~ /^@/;
407 2         6 $assert_msg = qq{\\\"$condition\\\"};
408             } elsif (defined $msg and not defined $condition) {
409 0 0       0 $msg = substr($msg, 1) if $msg =~ /^@/;
410 0         0 $assert_msg = qq{\\\"$msg\\\"};
411             } else {
412 0         0 $assert_msg = qq{\\\"user requested an assert\\\"};
413             }
414             }
415              
416             return << "..."
417             \t;; break if the condition evaluates to false
418             \t.assert "$assert_msg"
419             \tnop ;; needed for the assert
420             ...
421 29         101 }
422              
423             sub stimulate {
424 9     9 0 41 my $self = shift;
425 9         14 my $pin = shift;
426 9         15 my %hh = ();
427 9         15 foreach my $href (@_) {
428 11         46 %hh = (%hh, %$href);
429             }
430 9         12 my $period = '';
431 9 100 66     37 $period = $hh{EVERY} if (defined $hh{EVERY} and length $hh{EVERY});
432 9 100 66     76 $period = qq{\t.sim "period $period"} if (defined $period and length $period);
433 9         57 my $wave = '';
434 9         12 my $wave_type = 'digital';
435 9 50 33     52 if (exists $hh{WAVE} and ref $hh{WAVE} eq 'ARRAY') {
436 9         14 my $arr = $hh{WAVE};
437 9 50       56 $wave = "\t.sim \"{ " . join(',', @$arr) . " }\"" if scalar @$arr;
438 9         12 my $ad = 0;
439 9         20 foreach (@$arr) {
440 66 100       305 $ad |= 1 unless /^\d+$/;
441             }
442 9 100       42 $wave_type = 'analog' if $ad;
443             }
444 9   50     223 my $start = $hh{START} || 0;
445 9         97 $start = qq{\t.sim "start_cycle $start"};
446 9   50     151 my $init = $hh{INITIAL} || 0;
447 9         89 $init = qq{\t.sim "initial_state $init"};
448 9         122 my $num = $self->stimulus_count;
449 9         35 $self->stimulus_count($num + 1);
450 9         382 my $node = "stim$num$pin";
451 9         120 my $simpin = $self->_get_simport($pin);
452             return << "..."
453             \t.sim \"echo creating stimulus number $num\"
454             \t.sim \"stimulus asynchronous_stimulus\"
455             $init
456             $start
457             \t.sim \"$wave_type\"
458             $period
459             $wave
460             \t.sim \"name stim$num\"
461             \t.sim \"end\"
462             \t.sim \"echo done creating stimulus number $num\"
463             \t.sim \"node $node\"
464             \t.sim \"attach $node stim$num $simpin\"
465             ...
466 9         21 }
467              
468             sub get_autorun_code {
469 12     12 0 94 return qq{\t.sim "run"\n};
470             }
471              
472             sub autorun {
473 12     12 0 59 my $self = shift;
474 12         50 $self->should_autorun(1);
475 12         41 return "\t;;;; will autorun on start\n";
476             }
477              
478             sub stopwatch {
479 1     1 0 5 my ($self, $rollover) = @_;
480 1         1 my $code = qq{\t.sim "stopwatch.enable = true"\n};
481 1 50       5 $code .= qq{\t.sim "stopwatch.rollover = $rollover"\n} if defined $rollover;
482 1 50       3 $code .= qq{\t.sim "break stopwatch"\n} if defined $rollover;
483 1         2 return $code;
484             }
485              
486             sub attach {
487 3     3 0 22 my $self = shift;
488 3 50       10 return unless @_;
489 3         4 my $pin = shift;
490 3         5 my $code = '';
491 3 50       14 if ($pin =~ /US?ART/) {
492             # TX - connect to UART
493             # RX - connect to UART but also send it data
494 3 50       12 unless ($self->pic->doesrole('USART')) {
495 0         0 carp "PIC ", $self->pic->type, " does not do USART";
496 0         0 return;
497             }
498 3 50       13 my $baudrate = shift if @_;
499 3 100       9 my $loopback = shift if @_;
500 3 50       15 my $key = ($pin =~ /^UART/) ? 'uart' : 'usart';
501             $baudrate = $self->pic->code_config->{$key}->{baud} unless defined
502 3 50       8 $baudrate;
503 3 50       8 $baudrate = 9600 unless defined $baudrate;
504 3         12 my $id = $self->node_count;
505 3         18 $self->node_count($id + 1);
506 3         209 my $ipin = $self->pic->usart_pins->{async_in};
507 3         19 my $rxport = $self->_get_simport($ipin);
508 3         7 my $opin = $self->pic->usart_pins->{async_out};
509 3         15 my $txport = $self->_get_simport($opin);
510 3 50 33     9 return unless (exists $self->pic->pins->{$ipin} and exists $self->pic->pins->{$opin});
511 3         37 $code .= qq{\t.sim "module load usart U$id"\n};
512 3         78 $code .= qq{\t.sim "node TX_U$id"\n};
513 3         39 $code .= qq{\t.sim "node RX_U$id"\n};
514 3         50 $code .= qq{\t.sim "attach TX_U$id $txport U$id.RXPIN"\n};
515 3         67 $code .= qq{\t.sim "attach RX_U$id $rxport U$id.TXPIN"\n};
516 3         64 $code .= qq{\t.sim "U$id.txbaud = $baudrate"\n};
517 3         65 $code .= qq{\t.sim "U$id.rxbaud = $baudrate"\n};
518 3         35 my $x = 500;
519 3         12 my $y = 50 + 50 * $id;
520 3         264 $code .= qq{\t.sim "U$id.xpos = $x"\n};
521 3         72 $code .= qq{\t.sim "U$id.ypos = $y"\n};
522 3 100       69 if (defined $loopback) {
523 2 50 33     16 if (ref $loopback eq 'HASH' and $loopback->{string} =~ /loopback/i) {
524 2         6 $code .= qq{\t.sim "U$id.loop = true"\n};
525             }
526             }
527             }
528 3         31 return $code;
529             }
530              
531             1;
532              
533             =encoding utf8
534              
535             =head1 NAME
536              
537             VIC::Receiver
538              
539             =head1 SYNOPSIS
540              
541             The Pegex::Receiver class for handling the grammar.
542              
543             =head1 DESCRIPTION
544              
545             INTERNAL CLASS.
546              
547             =head1 AUTHOR
548              
549             Vikas N Kumar
550              
551             =head1 COPYRIGHT
552              
553             Copyright (c) 2014. Vikas N Kumar
554              
555             This program is free software; you can redistribute it and/or modify it
556             under the same terms as Perl itself.
557              
558             See http://www.perl.com/perl/misc/Artistic.html
559              
560             =cut