File Coverage

blib/lib/VIC/PIC/Functions/Operations.pm
Criterion Covered Total %
statement 100 124 80.6
branch 26 52 50.0
condition 2 4 50.0
subroutine 16 20 80.0
pod 0 5 0.0
total 144 205 70.2


line stmt bran cond sub pod time code
1             package VIC::PIC::Functions::Operations;
2 31     31   16673 use strict;
  31         43  
  31         806  
3 31     31   114 use warnings;
  31         45  
  31         620  
4 31     31   111 use bigint;
  31         39  
  31         979  
5             our $VERSION = '0.31';
6             $VERSION = eval $VERSION;
7 31     31   20159 use Carp;
  31         52  
  31         2214  
8 31     31   128 use POSIX ();
  31         43  
  31         470  
9 31     31   118 use Moo::Role;
  31         76  
  31         174  
10              
11             sub _macro_delay_var {
12 16     16   54 return <<'...';
13             ;;;;;; DELAY FUNCTIONS ;;;;;;;
14              
15             VIC_VAR_DELAY_UDATA udata
16             VIC_VAR_DELAY res 3
17              
18             ...
19             }
20              
21             sub _macro_delay_us {
22 12     12   62 return <<'...';
23             ;; 1MHz => 1us per instruction
24             ;; return, goto and call are 2us each
25             ;; hence each loop iteration is 3us
26             ;; the rest including movxx + return = 2us
27             ;; hence usecs - 6 is used
28             m_delay_us macro usecs
29             local _delay_usecs_loop_0
30             variable usecs_1 = 0
31             variable usecs_2 = 0
32             if (usecs > D'6')
33             usecs_1 = usecs / D'3' - 2
34             usecs_2 = usecs % D'3'
35             movlw usecs_1
36             movwf VIC_VAR_DELAY
37             decfsz VIC_VAR_DELAY, F
38             goto $ - 1
39             while usecs_2 > 0
40             goto $ + 1
41             usecs_2--
42             endw
43             else
44             usecs_1 = usecs
45             while usecs_1 > 0
46             nop
47             usecs_1--
48             endw
49             endif
50             endm
51             ...
52             }
53              
54             sub _macro_delay_wus {
55 0     0   0 return <<'...';
56             m_delay_wus macro
57             local _delayw_usecs_loop_0
58             movwf VIC_VAR_DELAY
59             _delayw_usecs_loop_0:
60             decfsz VIC_VAR_DELAY, F
61             goto _delayw_usecs_loop_0
62             endm
63             ...
64             }
65              
66             sub _macro_delay_ms {
67 12     12   49 return <<'...';
68             ;; 1MHz => 1us per instruction
69             ;; each loop iteration is 3us each
70             ;; there are 2 loops, one for (768 + 3) us
71             ;; and one for the rest in ms
72             ;; we add 3 instructions for the outer loop
73             ;; number of outermost loops = msecs * 1000 / 771 = msecs * 13 / 10
74             m_delay_ms macro msecs
75             local _delay_msecs_loop_0, _delay_msecs_loop_1, _delay_msecs_loop_2
76             variable msecs_1 = 0
77             variable msecs_2 = 0
78             msecs_1 = (msecs * D'1000') / D'771'
79             msecs_2 = ((msecs * D'1000') % D'771') / 3 - 2;; for 3 us per instruction
80             movlw msecs_1
81             movwf VIC_VAR_DELAY + 1
82             _delay_msecs_loop_1:
83             clrf VIC_VAR_DELAY ;; set to 0 which gets decremented to 0xFF
84             _delay_msecs_loop_0:
85             decfsz VIC_VAR_DELAY, F
86             goto _delay_msecs_loop_0
87             decfsz VIC_VAR_DELAY + 1, F
88             goto _delay_msecs_loop_1
89             if msecs_2 > 0
90             ;; handle the balance
91             movlw msecs_2
92             movwf VIC_VAR_DELAY
93             _delay_msecs_loop_2:
94             decfsz VIC_VAR_DELAY, F
95             goto _delay_msecs_loop_2
96             nop
97             endif
98             endm
99             ...
100             }
101              
102             sub _macro_delay_wms {
103 4     4   8 return <<'...';
104             m_delay_wms macro
105             local _delayw_msecs_loop_0, _delayw_msecs_loop_1
106             movwf VIC_VAR_DELAY + 1
107             _delayw_msecs_loop_1:
108             clrf VIC_VAR_DELAY ;; set to 0 which gets decremented to 0xFF
109             _delayw_msecs_loop_0:
110             decfsz VIC_VAR_DELAY, F
111             goto _delayw_msecs_loop_0
112             decfsz VIC_VAR_DELAY + 1, F
113             goto _delayw_msecs_loop_1
114             endm
115             ...
116             }
117              
118             sub _macro_delay_s {
119 12     12   58 return <<'...';
120             ;; 1MHz => 1us per instruction
121             ;; each loop iteration is 3us each
122             ;; there are 2 loops, one for (768 + 3) us
123             ;; and one for the rest in ms
124             ;; we add 3 instructions for the outermost loop
125             ;; 771 * 256 + 3 = 197379 ~= 200000
126             ;; number of outermost loops = seconds * 1000000 / 200000 = seconds * 5
127             m_delay_s macro secs
128             local _delay_secs_loop_0, _delay_secs_loop_1, _delay_secs_loop_2
129             local _delay_secs_loop_3
130             variable secs_1 = 0
131             variable secs_2 = 0
132             variable secs_3 = 0
133             variable secs_4 = 0
134             secs_1 = (secs * D'1000000') / D'197379'
135             secs_2 = ((secs * D'1000000') % D'197379') / 3
136             secs_4 = (secs_2 >> 8) & 0xFF - 1
137             secs_3 = 0xFE
138             movlw secs_1
139             movwf VIC_VAR_DELAY + 2
140             _delay_secs_loop_2:
141             clrf VIC_VAR_DELAY + 1 ;; set to 0 which gets decremented to 0xFF
142             _delay_secs_loop_1:
143             clrf VIC_VAR_DELAY ;; set to 0 which gets decremented to 0xFF
144             _delay_secs_loop_0:
145             decfsz VIC_VAR_DELAY, F
146             goto _delay_secs_loop_0
147             decfsz VIC_VAR_DELAY + 1, F
148             goto _delay_secs_loop_1
149             decfsz VIC_VAR_DELAY + 2, F
150             goto _delay_secs_loop_2
151             if secs_4 > 0
152             movlw secs_4
153             movwf VIC_VAR_DELAY + 1
154             _delay_secs_loop_3:
155             clrf VIC_VAR_DELAY
156             decfsz VIC_VAR_DELAY, F
157             goto $ - 1
158             decfsz VIC_VAR_DELAY + 1, F
159             goto _delay_secs_loop_3
160             endif
161             if secs_3 > 0
162             movlw secs_3
163             movwf VIC_VAR_DELAY
164             decfsz VIC_VAR_DELAY, F
165             goto $ - 1
166             endif
167             endm
168             ...
169             }
170              
171             sub _macro_delay_ws {
172 0     0   0 return <<'...';
173             m_delay_ws macro
174             local _delayw_secs_loop_0, _delayw_secs_loop_1, _delayw_secs_loop_2
175             movwf VIC_VAR_DELAY + 2
176             _delayw_secs_loop_2:
177             clrf VIC_VAR_DELAY + 1 ;; set to 0 which gets decremented to 0xFF
178             _delayw_secs_loop_1:
179             clrf VIC_VAR_DELAY ;; set to 0 which gets decremented to 0xFF
180             _delayw_secs_loop_0:
181             decfsz VIC_VAR_DELAY, F
182             goto _delayw_secs_loop_0
183             decfsz VIC_VAR_DELAY + 1, F
184             goto _delayw_secs_loop_1
185             decfsz VIC_VAR_DELAY + 2, F
186             goto _delayw_secs_loop_2
187             endm
188             ...
189             }
190              
191             sub _delay_w {
192 4     4   6 my ($self, $unit, $varname) = @_;
193 4         6 my $funcs = {};
194 4         17 my $macros = { m_delay_var => $self->_macro_delay_var };
195 4         8 my $fn = "_delay_w$unit";
196 4         10 my $mac = "m_delay_w$unit";
197 4         6 my $mack = "_macro_delay_w$unit";
198 4         10 my $code = << "...";
199             \tmovf $varname, W
200             \tcall $fn
201             ...
202 4         10 $funcs->{$fn} = << "....";
203             \t$mac
204             \treturn
205             ....
206 4         17 $macros->{$mac} = $self->$mack;
207 4 50       20 return wantarray ? ($code, $funcs, $macros) : $code;
208             }
209              
210             sub delay {
211 12     12 0 52 my ($self, $t) = @_;
212 12 50       51 return unless $self->doesrole('Operations');
213 12 50       62 return $self->_delay_w(s => uc($t)) unless $t =~ /^\d+$/;
214 12 50       112 return '' if $t <= 0;
215             # divide the time into component seconds, milliseconds and microseconds
216 12         655 my $sec = POSIX::floor($t / 1e6);
217 12         988 my $ms = POSIX::floor(($t - $sec * 1e6) / 1000);
218 12         3006 my $us = $t - $sec * 1e6 - $ms * 1000;
219 12         3693 my $code = '';
220 12         20 my $funcs = {};
221             # return all as part of the code always
222 12         43 my $macros = {
223             m_delay_var => $self->_macro_delay_var,
224             m_delay_s => $self->_macro_delay_s,
225             m_delay_ms => $self->_macro_delay_ms,
226             m_delay_us => $self->_macro_delay_us,
227             };
228             ## more than one function could be called so have them separate
229 12 100       34 if ($sec > 0) {
230 5         319 my $fn = "_delay_${sec}s";
231 5         16 $code .= "\tcall $fn\n";
232 5         5 my $fncode = '';
233 5         7 my $siter = $sec;
234             ## the number 50 comes from 255 * 197379 / 1e6
235 5         9 while ($siter >= 50) {
236 0         0 $fncode .= "\tm_delay_s D'50'\n";
237 0         0 $siter -= 50;
238             }
239 5 50       281 $fncode .= "\tm_delay_s D'$siter'\n" if $siter > 0;
240 5         283 $fncode .= "\treturn\n";
241 5         12 $funcs->{$fn} = $fncode;
242             }
243 12 100       737 if ($ms > 0) {
244 6         410 my $fn = "_delay_${ms}ms";
245 6         15 $code .= "\tcall $fn\n";
246 6         10 my $fncode = '';
247 6         9 my $msiter = $ms;
248             ## the number 196 comes from 255 * 771 / 1000
249 6         19 while ($msiter >= 196) {
250 0         0 $fncode .= "\tm_delay_ms D'196'\n";
251 0         0 $msiter -= 196;
252             }
253 6 50       400 $fncode .= "\tm_delay_ms D'$msiter'\n" if $msiter > 0;
254 6         393 $fncode .= "\treturn\n";
255 6         17 $funcs->{$fn} = $fncode;
256             }
257 12 100       500 if ($us > 0) {
258             # for less than 6 us we just inline the code
259 1 50       16 if ($us <= 6) {
260 0         0 $code .= "\tm_delay_us D'$us'\n";
261             } else {
262 1         16 my $fn = "_delay_${us}us";
263 1         18 $code .= "\tcall $fn\n";
264 1         1 my $fncode = '';
265 1         2 my $usiter = $us;
266             ## the number 771 comes from (255 + 2) * 3
267 1         2 while ($usiter >= 771) {
268 0         0 $fncode .= "\tm_delay_us D'771'\n";
269 0         0 $usiter -= 771;
270             }
271 1 50       17 $fncode .= "\tm_delay_us D'$usiter'\n" if $usiter > 0;
272 1         27 $fncode .= "\treturn\n";
273 1         3 $funcs->{$fn} = $fncode;
274             }
275             }
276 12 50       244 return wantarray ? ($code, $funcs, $macros) : $code;
277             }
278              
279             sub delay_s {
280 0     0 0 0 my ($self, $t) = @_;
281 0 0       0 return $self->delay($t * 1e6) if $t =~ /^\d+$/;
282 0         0 return $self->_delay_w(s => uc($t));
283             }
284              
285             sub delay_ms {
286 4     4 0 27 my ($self, $t) = @_;
287 4 50       18 return $self->delay($t * 1000) if $t =~ /^\d+$/;
288 4         16 return $self->_delay_w(ms => uc($t));
289             }
290              
291             sub delay_us {
292 0     0 0 0 my ($self, $t) = @_;
293 0 0       0 return $self->delay($t) if $t =~ /^\d+$/;
294 0         0 return $self->_delay_w(us => uc($t));
295             }
296              
297             sub _macro_debounce_var {
298 4     4   11 return <<'...';
299             ;;;;;; VIC_VAR_DEBOUNCE VARIABLES ;;;;;;;
300              
301             VIC_VAR_DEBOUNCE_VAR_IDATA idata
302             ;; initialize state to 1
303             VIC_VAR_DEBOUNCESTATE db 0x01
304             ;; initialize counter to 0
305             VIC_VAR_DEBOUNCECOUNTER db 0x00
306              
307             ...
308             }
309              
310             sub debounce {
311 4     4 0 31 my ($self, $inp, %action) = @_;
312 4 50       14 return unless $self->doesroles(qw(Operations Chip CodeGen GPIO));
313 4 50       30 unless (exists $self->registers->{STATUS}) {
314 0         0 carp $self->type, " does not have the STATUS register";
315 0         0 return;
316             }
317 4         7 my $action_label = $action{ACTION};
318 4         7 my $end_label = $action{END};
319 4 50       10 return unless $action_label;
320 4 50       10 return unless $end_label;
321 4         6 my ($port, $portbit);
322 4 50       17 if (exists $self->pins->{$inp}) {
    0          
323 4         18 my $ipin = $self->get_input_pin($inp);
324 4 50       10 unless (defined $ipin) {
325 0         0 carp "Cannot find $inp in the list of GPIO ports or pins";
326 0         0 return;
327             } else {
328 4         6 my $tris;
329 4         5 ($port, $tris, $portbit) = @{$self->input_pins->{$inp}};
  4         20  
330             }
331             } elsif (exists $self->registers->{$inp}) {
332 0         0 $port = $inp;
333 0         0 $portbit = 0;
334 0         0 carp "Port $port has been supplied. Assuming portbit to debounce is $portbit";
335             } else {
336 0         0 carp "Cannot find $inp in the list of ports or pins";
337 0         0 return;
338             }
339             # incase the user does weird stuff override the count and delay
340 4   50     23 my $debounce_count = $self->code_config->{debounce}->{count} || 1;
341 4   50     22 my $debounce_delay = $self->code_config->{debounce}->{delay} || 1000;
342 4         92 my ($deb_code, $funcs, $macros) = $self->delay($debounce_delay);
343 4 50       12 $macros = {} unless defined $macros;
344 4 50       13 $funcs = {} unless defined $funcs;
345 4 50       9 $deb_code = 'nop' unless defined $deb_code;
346 4         22 $macros->{m_debounce_var} = $self->_macro_debounce_var;
347 4         22 $debounce_count = sprintf "0x%02X", $debounce_count;
348 4         40 my $code = <<"...";
349             \t;;; generate code for debounce $port<$portbit>
350             $deb_code
351             \t;; has debounce state changed to down (bit 0 is 0)
352             \t;; if yes go to debounce-state-down
353             \tbtfsc VIC_VAR_DEBOUNCESTATE, 0
354             \tgoto _debounce_state_up
355             _debounce_state_down:
356             \tclrw
357             \tbtfss $port, $portbit
358             \t;; increment and move into counter
359             \tincf VIC_VAR_DEBOUNCECOUNTER, 0
360             \tmovwf VIC_VAR_DEBOUNCECOUNTER
361             \tgoto _debounce_state_check
362              
363             _debounce_state_up:
364             \tclrw
365             \tbtfsc $port, $portbit
366             \tincf VIC_VAR_DEBOUNCECOUNTER, 0
367             \tmovwf VIC_VAR_DEBOUNCECOUNTER
368             \tgoto _debounce_state_check
369              
370             _debounce_state_check:
371             \tmovf VIC_VAR_DEBOUNCECOUNTER, W
372             \txorlw $debounce_count
373             \t;; is counter == $debounce_count ?
374             \tbtfss STATUS, Z
375             \tgoto $end_label
376             \t;; after $debounce_count straight, flip direction
377             \tcomf VIC_VAR_DEBOUNCESTATE, 1
378             \tclrf VIC_VAR_DEBOUNCECOUNTER
379             \t;; was it a key-down
380             \tbtfss VIC_VAR_DEBOUNCESTATE, 0
381             \tgoto $end_label
382             \tgoto $action_label
383             $end_label:\n
384             ...
385 4 50       46 return wantarray ? ($code, $funcs, $macros) : $code;
386             }
387              
388             1;
389             __END__