File Coverage

blib/lib/Device/Chip/AVR_HVSP/FuseInfo.pm
Criterion Covered Total %
statement 11 37 29.7
branch 0 8 0.0
condition n/a
subroutine 4 8 50.0
pod 4 4 100.0
total 19 57 33.3


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-2015 -- leonerd@leonerd.org.uk
5              
6             package Device::Chip::AVR_HVSP::FuseInfo 0.06;
7              
8 1     1   1204 use v5.26;
  1         4  
9 1     1   5 use warnings;
  1         1  
  1         27  
10              
11 1     1   5 use Carp;
  1         2  
  1         55  
12              
13 1     1   6 use Struct::Dumb qw( readonly_struct );
  1         2  
  1         5  
14              
15             readonly_struct Fuse => [qw( name offset mask caption values )];
16             readonly_struct FuseEnumValue => [qw( name value caption )];
17              
18             my %info_for;
19              
20             =head1 NAME
21              
22             C - information about device fuses
23              
24             =head1 DESCRIPTION
25              
26             Objects in this class contain information about the configuration fuses of a
27             single F HVSP-programmable device. These instances may be useful for
28             encoding and decoding the fuse bytes, for display or other purposes in some
29             user-interactive manner.
30              
31             =cut
32              
33             =head1 CONSTRUCTOR
34              
35             =head2 for_part
36              
37             $fuseinfo = Device::Chip::AVR_HVSP::FuseInfo->for_part( $part )
38              
39             Returns a new C instance containing
40             information about the fuses for the given part name.
41              
42             =cut
43              
44             sub for_part
45             {
46 0     0 1   my $class = shift;
47 0           my ( $part ) = @_;
48              
49 0 0         $info_for{$part} or croak "No defined fuses for $part";
50              
51 0           return bless { %{ $info_for{$part} } }, $class;
  0            
52             }
53              
54             =head1 METHODS
55              
56             =cut
57              
58             =head2 fuses
59              
60             @fuses = $fuseinfo->fuses
61              
62             Returns a list of objects, each one representing a single configuration fuse.
63             Each has the following fields:
64              
65             $fuse->name
66             $fuse->offset
67             $fuse->mask
68             $fuse->caption
69             @values = $fuse->values
70              
71             If the C method gives a non-empty list of values, then the fuse is an
72             enumeration; otherwise it is a simple boolean true/false flag. For enumeration
73             fuses, each value item has the following fields:
74              
75             $value->name
76             $value->value
77             $value->caption
78              
79             =cut
80              
81             sub fuses
82             {
83 0     0 1   my $self = shift;
84 0           return @{ $self->{fuses} };
  0            
85             }
86              
87             =head2 unpack
88              
89             %fields = $fuseinfo->unpack( $bytes )
90              
91             Given a byte string containing all the fuses read from the device, unpacks
92             them and returns a key-value list giving the current value of every fuse.
93              
94             =cut
95              
96             sub unpack
97             {
98 0     0 1   my $self = shift;
99 0           my ( $bytes ) = @_;
100              
101 0           my %ret;
102 0           foreach my $f ( $self->fuses ) {
103 0           my $bits = ord( substr $bytes, $f->offset, 1 ) & $f->mask;
104              
105 0           $ret{$f->name} = $bits;
106             }
107              
108 0           return %ret;
109             }
110              
111             =head2 pack
112              
113             $bytes = $fuseinfo->pack( %fields )
114              
115             Given a key-value list containing fuse values, packs them into a byte string
116             suitable to write onto the device and returns it.
117              
118             =cut
119              
120             sub pack
121             {
122 0     0 1   my $self = shift;
123 0           my %fuses = @_;
124              
125 0           my $bytes = ~$self->{mask};
126 0           foreach my $f ( $self->fuses ) {
127 0           my $v = $fuses{$f->name};
128              
129 0 0         if( $f->values ) {
130             # Value check enum fuse
131 0 0         croak "Invalid value for ${\$f->name}: $v" if $v & ~$f->mask;
  0            
132             }
133             else {
134 0 0         $v = $f->mask if $v;
135             }
136              
137 0           substr( $bytes, $f->offset, 1 ) |= chr( $f->mask & $v );
138             }
139              
140 0           return $bytes;
141             }
142              
143             my $info;
144              
145             LINE: while( my $line = ) {
146             if( $line =~ m/^DEVICE name=(\S+)$/ ) {
147             $info = {} if keys %$info; # new device
148             $info_for{$1} = $info;
149             }
150             elsif( $line =~ m/^MASK (\d+) (\d+)$/ ) {
151             $info->{mask} ||= "";
152             $info->{mask} .= "\0" until length $info->{mask} >= $1;
153             substr( $info->{mask}, $1, 1 ) = chr $2;
154             }
155             elsif( $line =~ m/^BIT (\S+) (\d+) (\d+): (.*)$/ ) {
156             push @{ $info->{fuses} }, Fuse( $1, $2, $3+0, $4, undef );
157             }
158             elsif( $line =~ m/^ENUM (\S+) (\d+) (\d+): (.*)$/ ) {
159             my $values = [];
160             push @{ $info->{fuses} }, Fuse( $1, $2, $3+0, $4, $values );
161              
162             while( $line = ) {
163             $line =~ m/^ VALUE (\S+) (\d+): (.*)$/ or redo LINE;
164             push @$values, FuseEnumValue( $1, $2, $3 );
165             }
166             }
167             }
168              
169             =head1 AUTHOR
170              
171             Paul Evans
172              
173             =cut
174              
175             0x55AA;
176              
177             __DATA__