File Coverage

blib/lib/Device/BusPirate/Chip/AVR_HVSP.pm
Criterion Covered Total %
statement 30 227 13.2
branch 0 32 0.0
condition 0 16 0.0
subroutine 10 78 12.8
pod 11 15 73.3
total 51 368 13.8


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2014 -- leonerd@leonerd.org.uk
5              
6             package Device::BusPirate::Chip::AVR_HVSP;
7              
8 1     1   570 use strict;
  1         1  
  1         34  
9 1     1   4 use warnings;
  1         1  
  1         28  
10 1     1   10 use base qw( Device::BusPirate::Chip );
  1         2  
  1         510  
11              
12             our $VERSION = '0.01';
13              
14 1     1   332 use Carp;
  1         1  
  1         66  
15              
16 1     1   466 use Future::Utils qw( repeat );
  1         1553  
  1         59  
17              
18 1     1   5 use constant CHIP => "AVR_HVSP";
  1         2  
  1         54  
19 1     1   3 use constant MODE => "BB";
  1         2  
  1         670  
20              
21             =head1 NAME
22              
23             C - high-voltage serial programming for F chips
24              
25             =head1 DESCRIPTION
26              
27             This L subclass allows interaction with an F
28             microcontroller of the F family in high-voltage serial programming
29             (HVSP) mode. It is particularly useful for configuring fuses or working with a
30             chip with the C fuse programmed, because in such cases a regular ISP
31             programmer cannot be used.
32              
33             =head2 CONNECTIONS
34              
35             To use this module, make the following connections between the F
36             (with colours of common cable types), and the F chip (with example pin
37             numbers for some common devices):
38              
39             Bus Pirate | Sparkfun | Seeed |:| ATtiny | tiny84 | tiny85
40             -----------+----------+----------+-+--------+--------+-------
41             MISO | brown | black |:| SDO | 9 | 7
42             CS | red | white |:| SII | 8 | 6
43             MOSI | orange | grey |:| SDI | 7 | 5
44             CLK | yellow | purple |:| SCI | 2 | 2
45             AUX | green | blue |:| +12V supply - see below
46             | | |:| RESET | 4 | 1
47             +5V | grey | orange |:| Vcc | 1 | 8
48             GND | black | brown |:| GND | 14 | 4
49              
50             The C line from the F will need to be able to control a +12V
51             supply to the C pin of the F chip. It should be active-high,
52             and can be achieved by a two-stage NPN-then-PNP transistor arrangement.
53              
54             Additionally, the C pin and the C to C pins of 14-pin devices
55             will need a pull-down to ground of around 100Ohm to 1kOhm.
56              
57             =cut
58              
59             =head1 METHODS
60              
61             The following methods documented with a trailing call to C<< ->get >> return
62             L instances.
63              
64             =cut
65              
66             # TODO: This needs to be migrated to Device::BusPirate itself
67             sub _enter_mutex
68             {
69 0     0     my $self = shift;
70 0           my ( $code ) = @_;
71              
72 0   0       my $oldmtx = $self->{mutex} // $self->pirate->_new_future->done( $self );
73 0           $self->{mutex} = my $newmtx = $self->pirate->_new_future;
74              
75             $oldmtx->then( $code )
76             ->then_with_f( sub {
77 0     0     my $f = shift;
78 0           $newmtx->done( $self );
79 0           $f
80 0           });
81             }
82              
83             my $SDI = "mosi";
84             my $SII = "cs";
85             my $SCI = "clk";
86             my $SDO = "miso";
87              
88             sub mount
89             {
90 0     0 1   my $self = shift;
91 0           my ( $mode ) = @_;
92              
93             $self->SUPER::mount( $mode )
94             ->then( sub {
95 0     0     $mode->configure(
96             open_drain => 0,
97             );
98             })
99             ->then( sub {
100 0     0     $mode->write(
101             $SDI => 0,
102             $SII => 0,
103             $SCI => 0,
104             );
105             })
106             ->then( sub {
107             # Set input
108 0     0     $mode->read_miso;
109 0           });
110             }
111              
112             =head2 $chip->start->get
113              
114             Powers up the device, reads and checks the signature, ensuring it is a
115             recognised chip.
116              
117             =cut
118              
119             my %SIGNATURES = (
120             ATtiny24 => "1E910B",
121             ATtiny44 => "1E9207",
122             ATtiny84 => "1E930C",
123             );
124              
125             sub start
126             {
127 0     0 1   my $self = shift;
128              
129             Future->needs_all(
130             $self->power(1),
131             $self->aux(1),
132             )->then( sub {
133 0     0     $self->read_signature;
134             })->then( sub {
135 0     0     my ( $sig ) = @_;
136 0           $sig = uc unpack "H*", $sig;
137              
138             $SIGNATURES{$_} eq $sig and $self->{part} = $_, return Future->done( $self )
139 0   0       for keys %SIGNATURES;
140              
141 0           Future->fail( "Unrecognised signature $sig" );
142 0           });
143             }
144              
145             =head2 $chip->stop->get
146              
147             Shut down power to the device.
148              
149             =cut
150              
151             sub stop
152             {
153 0     0 1   my $self = shift;
154              
155 0           Future->needs_all(
156             $self->power(0),
157             $self->aux(0),
158             );
159             }
160              
161             =head2 $name = $chip->partname
162              
163             Returns the name of the chip whose signature was detected by the C
164             method.
165              
166             =cut
167              
168             sub partname
169             {
170 0     0 1   my $self = shift;
171 0           return $self->{part};
172             }
173              
174             sub _transfer
175             {
176 0     0     my $self = shift;
177              
178 0           my ( $sdi, $sii ) = @_;
179              
180 0           my $sdo = 0;
181 0           my $mode = $self->mode;
182              
183             # A "byte" transfer consists of 11 clock transitions; idle low. Each bit is
184             # clocked in from SDO on the falling edge of clocks 0 to 7, but clocked out
185             # of SDI and SII on clocks 1 to 8.
186             # We'll therefore toggle the clock 11 times; on each of the first 8 clocks
187             # we raise it, then simultaneously lower it, writing out the next out bits
188             # and reading in the input.
189             # Serial transfer is MSB first in both directions
190             #
191             # We cheat massively here and rely on pipeline ordering of the actual
192             # ->write calls, by writing all 22 of the underlying bytes to the Bus
193             # Pirate serial port, then waiting on all 22 bytes to come back.
194              
195 0 0         Future->needs_all( map {
196             my $mask = $_ < 8 ? (1 << 7-$_) : 0;
197              
198             Future->needs_all(
199             $mode->write( $SCI => 1 ),
200              
201             $mode->writeread(
202             $SDI => ( $sdi & $mask ),
203             $SII => ( $sii & $mask ),
204             $SCI => 0
205             )->on_done( sub {
206 0 0   0     $sdo |= $mask if shift->{$SDO};
207             })
208 0           )
209             } 0 .. 10 )
210 0     0     ->then( sub { Future->done( $sdo ) } );
  0            
211             }
212              
213             sub _await_SDO_high
214             {
215 0     0     my $self = shift;
216              
217 0           my $mode = $self->mode;
218              
219 0           my $count = 50;
220             repeat {
221 0 0   0     $count-- or return Future->fail( "Timeout waiting for device to ACK" );
222              
223 0           $mode->${\"read_$SDO"}
  0            
224 0 0   0     } until => sub { $_[0]->failure or $_[0]->get };
  0            
225             }
226              
227             # The AVR datasheet on HVSP does not name any of these operations, only
228             # giving them bit patterns. We'll use the names invented by RikusW. See also
229             # https://sites.google.com/site/megau2s/
230              
231             use constant {
232             # SII values
233 1         226 HVSP_CMD => 0x4C, # Command
234             HVSP_LLA => 0x0C, # Load Lo Address
235             HVSP_LHA => 0x1C, # Load Hi Address
236             HVSP_LLB => 0x2C, # Load Lo Byte
237             HVSP_LHB => 0x3C, # Load Hi Byte
238             HVSP_WLB => 0x64, # Write Lo Byte = WRL = WFU0
239             HVSP_WHB => 0x74, # Write Hi Byte = WRH = WFU1
240             HVSP_WFU2 => 0x66, # Write Extended Fuse
241             HVSP_RLB => 0x68, # Read Lo Byte
242             HVSP_RHB => 0x78, # Read Hi Byte
243             HVSP_RSIG => 0x68, # Read Signature
244             HVSP_RFU0 => 0x68, # Read Low Fuse
245             HVSP_RFU1 => 0x7A, # Read High Fuse
246             HVSP_RFU2 => 0x6A, # Read Extended Fuse
247             HVSP_REEP => 0x68, # Read EEPROM
248             HVSP_ROSC => 0x78, # Read Oscillator calibration
249             HVSP_RLCK => 0x78, # Read Lock
250             HVSP_PLH => 0x7D, # Program (?) Hi
251             HVSP_PLL => 0x6D, # Program (?) Lo
252             HVSP_ORM => 0x0C, # OR mask for SII to pulse actual read/write operation
253              
254             # HVSP_CMD Commands
255             CMD_CE => 0x80, # Chip Erase
256             CMD_WFUSE => 0x40, # Write Fuse
257             CMD_WLOCK => 0x20, # Write Lock
258             CMD_WFLASH => 0x10, # Write FLASH
259             CMD_WEEP => 0x11, # Write EEPROM
260             CMD_RSIG => 0x08, # Read Signature
261             CMD_RFUSE => 0x04, # Read Fuse
262             CMD_RFLASH => 0x02, # Read FLASH
263             CMD_REEP => 0x03, # Read EEPROM
264             CMD_ROSC => 0x08, # Read Oscillator calibration
265             CMD_RLOCK => 0x04, # Read Lock
266 1     1   5 };
  1         1  
267             # Some synonyms not found in the AVR ctrlstack software
268             use constant {
269 1         1550 HVSP_WLCK => HVSP_WLB, # Write Lock
270             HVSP_WFU0 => HVSP_WLB, # Write Low Fuse
271             HVSP_WFU1 => HVSP_WHB, # Write High Fuse
272 1     1   4 };
  1         1  
273              
274             =head2 $avr->chip_erase->get
275              
276             Performs an entire chip erase. This will clear the flash and EEPROM memories,
277             before resetting the lock bits. It does not affect the fuses.
278              
279             =cut
280              
281             sub chip_erase
282             {
283 0     0 0   my $self = shift;
284              
285             $self->_transfer( CMD_CE, HVSP_CMD )
286 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB ) })
287 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB|HVSP_ORM ) })
288 0     0     ->then( sub { $self->_await_SDO_high });
  0            
289             }
290              
291             =head2 $bytes = $avr->read_signature->get
292              
293             Reads the three device signature bytes and returns them in as a single binary
294             string.
295              
296             =cut
297              
298             sub read_signature
299             {
300 0     0 0   my $self = shift;
301              
302             $self->_transfer( CMD_RSIG, HVSP_CMD )->then( sub {
303 0     0     my @sig;
304             repeat {
305 0           my $byte = shift;
306             $self->_transfer( $byte, HVSP_LLA )
307 0           ->then( sub { $self->_transfer( 0, HVSP_RSIG ) } )
308 0           ->then( sub { $self->_transfer( 0, HVSP_RSIG|HVSP_ORM ) } )
309 0           ->on_done( sub { $sig[$byte] = shift; } );
  0            
310             } foreach => [ 0 .. 2 ],
311 0           otherwise => sub { Future->done( pack "C*", @sig ) };
  0            
312             })
313 0           }
314              
315             =head2 $byte = $avr->read_calibration->get
316              
317             Reads the calibration byte.
318              
319             =cut
320              
321             sub read_calibration
322             {
323 0     0 0   my $self = shift;
324              
325             $self->_transfer( CMD_ROSC, HVSP_CMD )
326 0     0     ->then( sub { $self->_transfer( 0, HVSP_LLA ) } )
327 0     0     ->then( sub { $self->_transfer( 0, HVSP_ROSC ) } )
328 0     0     ->then( sub { $self->_transfer( 0, HVSP_ROSC|HVSP_ORM ) } )
329             ->then( sub {
330 0     0     Future->done( chr $_[0] )
331 0           });
332             }
333              
334             =head2 $byte = $avr->read_lock->get
335              
336             Reads the lock byte.
337              
338             =cut
339              
340             sub read_lock
341             {
342 0     0 0   my $self = shift;
343              
344             $self->_transfer( CMD_RLOCK, HVSP_CMD )
345 0     0     ->then( sub { $self->_transfer( 0, HVSP_RLCK ) } )
346 0     0     ->then( sub { $self->_transfer( 0, HVSP_RLCK|HVSP_ORM ) } )
347             ->then( sub {
348 0     0     my ( $byte ) = @_;
349 0           Future->done( chr( $byte & 3 ) );
350 0           });
351             }
352              
353             =head2 $avr->write_lock( $byte )->get
354              
355             Writes the lock byte.
356              
357             =cut
358              
359             sub write_lock
360             {
361 0     0 1   my $self = shift;
362 0           my ( $byte ) = @_;
363              
364             $self->_transfer( CMD_WLOCK, HVSP_CMD )
365 0     0     ->then( sub { $self->_transfer( ( ord $byte ) & 3, HVSP_LLB ) })
366 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLCK ) })
367 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLCK|HVSP_ORM ) })
368 0     0     ->then( sub { $self->_await_SDO_high });
  0            
369             }
370              
371             =head2 $int = $avr->read_fuse_byte( $fuse )->get
372              
373             Reads one of the fuse bytes C, C, C, returning an
374             integer.
375              
376             =cut
377              
378             my %SII_FOR_FUSE_READ = (
379             lfuse => HVSP_RFU0,
380             hfuse => HVSP_RFU1,
381             efuse => HVSP_RFU2,
382             );
383              
384             sub read_fuse_byte
385             {
386 0     0 1   my $self = shift;
387 0           my ( $fuse ) = @_;
388              
389 0 0         my $sii = $SII_FOR_FUSE_READ{$fuse} or croak "Unrecognised fuse type '$fuse'";
390              
391             $self->_transfer( CMD_RFUSE, HVSP_CMD )
392 0     0     ->then( sub { $self->_transfer( 0, $sii ) } )
393 0     0     ->then( sub { $self->_transfer( 0, $sii|HVSP_ORM ) } )
394 0           }
395              
396             =head2 $avr->write_fuse_byte( $fuse, $byte )->get
397              
398             Writes one of the fuse bytes C, C, C from an integer.
399              
400             =cut
401              
402             my %SII_FOR_FUSE_WRITE = (
403             lfuse => HVSP_WFU0,
404             hfuse => HVSP_WFU1,
405             efuse => HVSP_WFU2,
406             );
407              
408             sub write_fuse_byte
409             {
410 0     0 1   my $self = shift;
411 0           my ( $fuse, $byte ) = @_;
412              
413 0 0         my $sii = $SII_FOR_FUSE_WRITE{$fuse} or croak "Unrecognised fuse type '$fuse'";
414              
415             $self->_transfer( CMD_WFUSE, HVSP_CMD )
416 0     0     ->then( sub { $self->_transfer( $byte, HVSP_LLB ) })
417 0     0     ->then( sub { $self->_transfer( 0, $sii ) })
418 0     0     ->then( sub { $self->_transfer( 0, $sii|HVSP_ORM ) })
419 0     0     ->then( sub { $self->_await_SDO_high });
  0            
420             }
421              
422             =head2 $byte = $avr->read_lfuse->get
423              
424             =head2 $byte = $avr->read_hfuse->get
425              
426             =head2 $byte = $avr->read_efuse->get
427              
428             Convenient shortcuts to reading the low, high and extended fuses directly,
429             returning a byte.
430              
431             =head2 $avr->write_lfuse( $byte )->get
432              
433             =head2 $avr->write_hfuse( $byte )->get
434              
435             =head2 $avr->write_efuse( $byte )->get
436              
437             Convenient shortcuts for writing the low, high and extended fuses directly,
438             from a byte.
439              
440             =cut
441              
442             foreach my $fuse (qw( lfuse hfuse efuse )) {
443 1     1   6 no strict 'refs';
  1         1  
  1         1659  
444             *{"read_$fuse"} = sub {
445             shift->read_fuse_byte( $fuse )
446 0     0     ->then( sub { Future->done( chr $_[0] ) });
  0            
447             };
448             *{"write_$fuse"} = sub {
449 0     0     $_[0]->write_fuse_byte( $fuse, ord $_[1] );
450             };
451             }
452              
453             my %FLASH_SIZES = (
454             # Part => [ $words/page, $pages ]
455             ATtiny24 => [ 16, 64 ],
456             ATtiny44 => [ 32, 64 ],
457             ATtiny84 => [ 32, 128 ],
458             );
459              
460             =head2 $bytes = $avr->read_flash( %args )->get
461              
462             Reads a range of the flash memory and returns it as a binary string.
463              
464             Takes the following optional arguments:
465              
466             =over 4
467              
468             =item start => INT
469              
470             =item stop => INT
471              
472             Address range to read. If omitted, reads the entire memory.
473              
474             =item bytes => INT
475              
476             Alternative to C; gives the nubmer of bytes (i.e. not words of flash)
477             to read.
478              
479             =back
480              
481             =cut
482              
483             sub read_flash
484             {
485 0     0 1   my $self = shift;
486 0           my %opts = @_;
487              
488 0 0         my $part = $self->{part} or croak "Cannot ->read_flash of an unrecognised part";
489 0           my ( $nwords, $npages ) = @{ $FLASH_SIZES{$part} };
  0            
490              
491 0   0       my $start = $opts{start} // 0;
492 0 0 0       my $stop = $opts{stop} //
493             $opts{bytes} ? $start + ( $opts{bytes}/2 )
494             : ( $nwords * $npages );
495              
496 0           my $bytes = "";
497              
498             $self->_transfer( CMD_RFLASH, HVSP_CMD )->then( sub {
499 0     0     my $cur_ahi = -1;
500              
501             repeat {
502 0           my ( $addr ) = @_;
503 0           my $alo = $addr & 0xff;
504 0           my $ahi = $addr >> 8;
505              
506             $self->_transfer( $alo, HVSP_LLA )
507 0 0         ->then( sub { $cur_ahi == $ahi ? Future->done
508             : $self->_transfer( $cur_ahi = $ahi, HVSP_LHA ) })
509 0           ->then( sub { $self->_transfer( 0, HVSP_RLB ) })
510 0           ->then( sub { $self->_transfer( 0, HVSP_RLB|HVSP_ORM ) })
511 0           ->then( sub { $bytes .= chr $_[0];
512 0           $self->_transfer( 0, HVSP_RHB ) })
513 0           ->then( sub { $self->_transfer( 0, HVSP_RHB|HVSP_ORM ) })
514 0           ->then( sub { $bytes .= chr $_[0];
515 0           Future->done; });
  0            
516             } foreach => [ $start .. $stop - 1 ],
517 0           otherwise => sub { Future->done( $bytes ) };
  0            
518 0           });
519             }
520              
521             =head2 $avr->write_flash( $bytes )->get
522              
523             Writes the flash memory from the binary string.
524              
525             =cut
526              
527             sub write_flash
528             {
529 0     0 1   my $self = shift;
530 0           my ( $bytes ) = @_;
531              
532 0 0         my $part = $self->{part} or croak "Cannot ->write_flash of an unrecognised part";
533 0           my ( $nwords, $npages )= @{ $FLASH_SIZES{$part} };
  0            
534 0           my $nbytes = $nwords * 2; # words are 16-bits
535              
536 0 0         croak "Cannot write - too large" if length $bytes > $nbytes * $npages;
537              
538             $self->_transfer( CMD_WFLASH, HVSP_CMD )->then( sub {
539 0     0     my @chunks = $bytes =~ m/(.{1,$nbytes})/gs;
540 0           my $addr = 0;
541              
542             repeat {
543 0           my $thisaddr = $addr;
544 0           $addr += $nwords;
545              
546 0           $self->_write_flash_page( $_[0], $thisaddr )
547 0           } foreach => \@chunks;
548             })
549 0     0     ->then( sub { $self->_transfer( 0, HVSP_CMD ) });
  0            
550             }
551              
552             sub _write_flash_page
553             {
554 0     0     my $self = shift;
555 0           my ( $bytes, $baseaddr ) = @_;
556              
557             (
558             repeat {
559 0     0     my $addr = $baseaddr + $_[0];
560 0           my $byte_lo = substr $bytes, $_[0]*2, 1;
561 0           my $byte_hi = substr $bytes, $_[0]*2 + 1, 1;
562              
563             # Datasheet disagrees with the byte value written in the final
564             # instruction. Datasheet says 6C even though the OR mask would yield
565             # the value 6E. It turns out emperically that either value works fine
566             # so for neatness of following other code patterns, we use 6E here.
567              
568             $self->_transfer( $addr & 0xff, HVSP_LLA )
569 0           ->then( sub { $self->_transfer( ord $byte_lo, HVSP_LLB ) })
570 0           ->then( sub { $self->_transfer( 0, HVSP_PLL ) })
571 0           ->then( sub { $self->_transfer( 0, HVSP_PLL|HVSP_ORM ) })
572 0           ->then( sub { $self->_transfer( ord $byte_hi, HVSP_LHB ) })
573 0           ->then( sub { $self->_transfer( 0, HVSP_PLH ) })
574 0           ->then( sub { $self->_transfer( 0, HVSP_PLH|HVSP_ORM ) })
575 0           } foreach => [ 0 .. length($bytes)/2 - 1 ]
576             )
577 0     0     ->then( sub { $self->_transfer( $baseaddr >> 8, HVSP_LHA ) })
578 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB ) })
579 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB|HVSP_ORM ) })
580 0     0     ->then( sub { $self->_await_SDO_high });
  0            
581             }
582              
583             my %EEPROM_SIZES = (
584             # Part => [ $words/page, $pages ]
585             ATtiny24 => [ 4, 32 ],
586             ATtiny44 => [ 4, 64 ],
587             ATtiny84 => [ 4, 128 ],
588             );
589              
590             =head2 $bytes = $avr->read_eeprom( %args )->get
591              
592             Reads a range of the EEPROM memory and returns it as a binary string.
593              
594             Takes the following optional arguments:
595              
596             =over 4
597              
598             =item start => INT
599              
600             =item stop => INT
601              
602             Address range to read. If omitted, reads the entire memory.
603              
604             =item bytes => INT
605              
606             Alternative to C; gives the nubmer of bytes to read.
607              
608             =back
609              
610             =cut
611              
612             sub read_eeprom
613             {
614 0     0 1   my $self = shift;
615 0           my %opts = @_;
616              
617 0 0         my $part = $self->{part} or croak "Cannot ->read_eeprom of an unrecognised part";
618 0           my ( $nwords, $npages ) = @{ $EEPROM_SIZES{$part} };
  0            
619              
620 0   0       my $start = $opts{start} // 0;
621 0 0 0       my $stop = $opts{stop} //
622             $opts{bytes} ? $start + $opts{bytes}
623             : ( $nwords * $npages );
624              
625 0           my $bytes = "";
626              
627             $self->_transfer( CMD_REEP, HVSP_CMD )->then( sub {
628 0     0     my $cur_ahi = -1;
629              
630             repeat {
631 0           my ( $addr ) = @_;
632 0           my $alo = $addr & 0xff;
633 0           my $ahi = $addr >> 8;
634              
635             $self->_transfer( $alo, HVSP_LLA )
636 0 0         ->then( sub { $cur_ahi == $ahi ? Future->done
637             : $self->_transfer( $cur_ahi = $ahi, HVSP_LHA ) } )
638 0           ->then( sub { $self->_transfer( 0, HVSP_REEP ) } )
639 0           ->then( sub { $self->_transfer( 0, HVSP_REEP|HVSP_ORM ) } )
640 0           ->then( sub { $bytes .= chr $_[0];
641 0           Future->done; });
  0            
642             } foreach => [ $start .. $stop - 1 ],
643 0           otherwise => sub { Future->done( $bytes ) };
  0            
644 0           });
645             }
646              
647             =head2 $avr->write_eeprom( $bytes )->get
648              
649             Writes the EEPROM memory from the binary string.
650              
651             =cut
652              
653             sub write_eeprom
654             {
655 0     0 1   my $self = shift;
656 0           my ( $bytes ) = @_;
657              
658 0 0         my $part = $self->{part} or croak "Cannot ->write_eeprom of an unrecognised part";
659 0           my ( $nwords, $npages ) = @{ $EEPROM_SIZES{$part} };
  0            
660              
661 0 0         croak "Cannot write - too large" if length $bytes > $nwords * $npages;
662              
663             $self->_transfer( CMD_WEEP, HVSP_CMD )->then( sub {
664 0     0     my @chunks = $bytes =~ m/(.{1,$nwords})/gs;
665 0           my $addr = 0;
666              
667             repeat {
668 0           my $thisaddr = $addr;
669 0           $addr += $nwords;
670              
671 0           $self->_write_eeprom_page( $_[0], $thisaddr )
672 0           } foreach => \@chunks;
673             })
674 0     0     ->then( sub { $self->_transfer( 0, HVSP_CMD ) });
  0            
675             }
676              
677             sub _write_eeprom_page
678             {
679 0     0     my $self = shift;
680 0           my ( $bytes, $baseaddr ) = @_;
681              
682             (
683             repeat {
684 0     0     my $addr = $baseaddr + $_[0];
685 0           my $byte = substr $bytes, $_[0], 1;
686              
687             # Datasheet disagrees with the byte value written in the final
688             # instruction. Datasheet says 6C even though the OR mask would yield
689             # the value 6E. It turns out emperically that either value works fine
690             # so for neatness of following other code patterns, we use 6E here.
691              
692             $self->_transfer( $addr & 0xff, HVSP_LLA )
693 0           ->then( sub { $self->_transfer( $addr >> 8, HVSP_LHA ) })
694 0           ->then( sub { $self->_transfer( ord $byte, HVSP_LLB ) })
695 0           ->then( sub { $self->_transfer( 0, HVSP_PLL ) })
696 0           ->then( sub { $self->_transfer( 0, HVSP_PLL|HVSP_ORM ) })
697 0           } foreach => [ 0 .. length($bytes) - 1 ]
698             )
699 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB ) })
700 0     0     ->then( sub { $self->_transfer( 0, HVSP_WLB|HVSP_ORM ) })
701 0     0     ->then( sub { $self->_await_SDO_high });
  0            
702             }
703              
704             =head1 AUTHOR
705              
706             Paul Evans
707              
708             =cut
709              
710             0x55AA;