File Coverage

blib/lib/Device/AVR/Info.pm
Criterion Covered Total %
statement 20 121 16.5
branch 0 10 0.0
condition 0 24 0.0
subroutine 7 38 18.4
pod 16 16 100.0
total 43 209 20.5


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-2021 -- leonerd@leonerd.org.uk
5              
6             package Device::AVR::Info;
7              
8 1     1   879 use strict;
  1         2  
  1         35  
9 1     1   5 use warnings;
  1         3  
  1         29  
10 1     1   24 use 5.010;
  1         4  
11              
12             our $VERSION = '0.03';
13              
14 1     1   5 use Carp;
  1         2  
  1         75  
15              
16 1     1   807 use XML::Smart;
  1         25120  
  1         55  
17 1     1   720 use Struct::Dumb 'readonly_struct';
  1         2162  
  1         6  
18              
19 1     1   683 use Device::AVR::Info::Module;
  1         3  
  1         2052  
20              
21             =head1 NAME
22              
23             C - load data from F F device files
24              
25             =head1 SYNOPSIS
26              
27             use Device::AVR::Info;
28              
29             my $avr = Device::AVR::Info->new_from_file( "devices/ATtiny84.xml" );
30              
31             printf "The signature of %s is %s\n",
32             $avr->name, $avr->signature;
33              
34             =head1 DESCRIPTION
35              
36             This module loads an parses "part info" XML files as supplied with F's
37             F, and provides convenient access to the data stored inside them.
38              
39             =cut
40              
41             =head1 CONSTRUCTORS
42              
43             =cut
44              
45             =head2 new_from_file
46              
47             $avr = Device::AVR::Info->new_from_file( $filename )
48              
49             Loads the device information from the given XML file.
50              
51             =cut
52              
53             sub new_from_file
54             {
55 0     0 1   my $class = shift;
56 0           my ( $filename ) = @_;
57              
58 0           my $root = XML::Smart->new( $filename );
59              
60 0           my $devices = $root->{"avr-tools-device-file"}{devices};
61 0 0         @$devices == 1 or
62             croak "Expected only one device, found " . scalar(@$devices);
63              
64             bless {
65             _device => $devices->[0]{device},
66             _modules => $root->{"avr-tools-device-file"}{modules}{module},
67 0           }, $class;
68             }
69              
70             sub _module_by_name
71             {
72 0     0     my $self = shift;
73 0           my ( $name ) = @_;
74              
75 0           foreach ( @{ $self->{_modules} } ) {
  0            
76 0 0         return Device::AVR::Info::Module->_new( $_ ) if $_->{name} eq $name;
77             }
78              
79 0           croak "No module named '$name' is defined";
80             }
81              
82             =head1 ACCESSORS
83              
84             =cut
85              
86             =head2 name
87              
88             $name = $avr->name
89              
90             The device name (e.g. "ATtiny84")
91              
92             =head2 architecture
93              
94             $architecture = $avr->architecture
95              
96             The device architecture (e.g. "AVR8")
97              
98             =head2 family
99              
100             $family = $avr->family
101              
102             The device family (e.g. "tinyAVR")
103              
104             =cut
105              
106 0     0 1   sub name { shift->{_device}{name} }
107 0     0 1   sub architecture { shift->{_device}{architecture} }
108 0     0 1   sub family { shift->{_device}{family} }
109              
110             =head2 interfaces
111              
112             =head2 interface
113              
114             @ifaces = $avr->interfaces
115              
116             $iface = $avr->interface( $name )
117              
118             Returns a list of interface instances, or a single one having the given name,
119             representing the programming interfaces supported by the device.
120              
121             Each is a structure of the following fields.
122              
123             $iface->name
124             $iface->type
125              
126             =cut
127              
128             readonly_struct Interface => [qw( type name )];
129              
130             sub interfaces
131             {
132 0     0 1   my $self = shift;
133 0           return @{ $self->{interfaces} //= [ map {
134 0           Interface( "$_->{type}", "$_->{name}" )
135 0   0       } @{ $self->{_device}{interfaces}{interface} } ] };
  0            
136             }
137              
138             sub interface
139             {
140 0     0 1   my $self = shift;
141 0           my ( $name ) = @_;
142 0   0       $_->name eq $name and return $_ for $self->interfaces;
143 0           return;
144             }
145              
146             =head2 memories
147              
148             =head2 memory
149              
150             @memories = $avr->memories
151              
152             $memory = $avr->memory( $name )
153              
154             Returns a list of memory instances, or a single one having the given name,
155             representing the available memories on the device.
156              
157             Each is a structure of the following fields.
158              
159             $memory->name
160             $memory->id
161             $memory->endianness
162             $memory->start # in bytes
163             $memory->size # in bytes
164             @segments = $memory->segments
165             $segment = $memory->segment($name)
166              
167             The C field returns a list of structures of the following fields:
168              
169             $seg->start
170             $seg->size
171             $seg->name
172             $seg->type
173             $seg->can_read
174             $seg->can_write
175             $seg->can_exec
176             $seg->pagesize
177              
178             Note that all sizes are given in bytes; for memories of 16-bit word-size,
179             divide this by 2 to obtain the size in words.
180              
181             =cut
182              
183             {
184             # Can't quite use Struct::Dumb because of list return inflation of 'segments'
185             package
186             Device::AVR::Info::_Memory;
187              
188 0     0     sub name { shift->[0] }
189 0     0     sub id { shift->[1] }
190 0     0     sub endianness { shift->[2] }
191 0     0     sub start { shift->[3] }
192 0     0     sub size { shift->[4] }
193 0     0     sub segments { @{ shift->[5] } }
  0            
194 0   0 0     sub segment { $_->name eq $_[1] and return $_ for $_[0]->segments; return }
  0            
195             }
196              
197             readonly_struct MemorySegment => [qw( start size name type can_read can_write can_exec pagesize )];
198              
199             sub memories
200             {
201 0     0 1   my $self = shift;
202 0           return @{ $self->{memories} //= [ map {
203             my @segments = exists $_->{"memory-segment"} ? map {
204 0           my $rw = $_->{rw};
205 0           MemorySegment( hex "$_->{start}", hex "$_->{size}", "$_->{name}", "$_->{type}",
206             scalar $rw =~ m/R/, scalar $rw =~ m/W/, !!"$_->{exec}", hex "$_->{pagesize}" );
207 0 0         } @{ $_->{"memory-segment"} } : ();
  0            
208              
209 0           bless [ "$_->{name}", "$_->{id}", "$_->{endianness}",
210             hex "$_->{start}", hex "$_->{size}", \@segments ], "Device::AVR::Info::_Memory";
211 0   0       } @{ $self->{_device}{"address-spaces"}{"address-space"} } ] };
  0            
212             }
213              
214             sub memory
215             {
216 0     0 1   my $self = shift;
217 0           my ( $name ) = @_;
218 0   0       $_->name eq $name and return $_ for $self->memories;
219 0           return;
220             }
221              
222             sub _memory_by_id
223             {
224 0     0     my $self = shift;
225 0           my ( $id ) = @_;
226 0   0       $_->id eq $id and return $_ for $self->memories;
227 0           return;
228             }
229              
230             =head2 interrupts
231              
232             =head2 interrupt
233              
234             @ints = $avr->interrupts
235              
236             $int = $avr->interrupt( $name )
237              
238             Returns a list of interrupt instances, or a single one having the given name,
239             representing the interrupt sources available on the device.
240              
241             Each is a structure of the following fields.
242              
243             $int->name
244             $int->index
245             $int->caption
246              
247             =cut
248              
249             readonly_struct Interrupt => [qw( name index caption )];
250              
251             sub interrupts
252             {
253 0     0 1   my $self = shift;
254 0           return @{ $self->{interrupts} //= [ map {
255 0           Interrupt( "$_->{name}", "$_->{index}", "$_->{caption}" )
256 0   0       } @{ $self->{_device}{interrupts}{interrupt} } ] };
  0            
257             }
258              
259             sub interrupt
260             {
261 0     0 1   my $self = shift;
262 0           my ( $name ) = @_;
263 0   0       $_->name eq $name and return $_ for $self->interrupts;
264 0           return;
265             }
266              
267             =head2 peripherals
268              
269             =head2 peripheral
270              
271             @periphs = $avr->peripherals
272              
273             $periph = $avr->peripheral( $name )
274              
275             Returns a list of peripheral instances, or a single one having the given name,
276             representing the peripherals or other miscellaneous information available on
277             the device.
278              
279             Each is a structure of the following fields.
280              
281             $periph->name
282             $periph->module # instance of Device::AVR::Info::Module
283             $periph->regname
284             $periph->regspace # instance of $memory
285              
286             @registers = $periph->registers
287             # instances of $register from Device::AVR::Info::Module
288              
289             =cut
290              
291             {
292             package
293             Device::AVR::Info::_Peripheral;
294              
295 0     0     sub name { shift->[0] }
296 0     0     sub module { shift->[1] }
297 0     0     sub regname { shift->[2] }
298 0     0     sub regoffset { shift->[3] }
299 0     0     sub regspace { shift->[4] }
300              
301             sub registers {
302 0     0     my $self = shift;
303 0           $self->module->_registers_offset( $self->regname, $self->regoffset );
304             }
305             }
306              
307             sub peripherals
308             {
309 0     0 1   my $self = shift;
310 0           return @{ $self->{peripherals} //= [ map {
311 0           my $module = $self->_module_by_name( "$_->{name}" );
312             map {
313 0           my $reggroup = $_->{"register-group"}[0];
314             bless [ "$_->{name}", $module,
315             "$reggroup->{'name-in-module'}", hex "$reggroup->{offset}",
316 0           $self->_memory_by_id( $reggroup->{"address-space"} ) ], "Device::AVR::Info::_Peripheral";
317 0           } @{ $_->{instance} };
  0            
318 0   0       } @{ $self->{_device}{peripherals}{module} } ] };
  0            
319             }
320              
321             sub peripheral
322             {
323 0     0 1   my $self = shift;
324 0           my ( $name ) = @_;
325              
326 0           $self->peripherals;
327              
328 0   0       $_->name eq $name and return $_ for @{ $self->{peripherals} };
  0            
329 0           return;
330             }
331              
332             =head2 property_groups
333              
334             @group_names = $avr->property_groups
335              
336             Returns (in no particular order) the names of the defined property groups.
337              
338             =head2 property_group
339              
340             \%values = $avr->property_group( $group_name )
341              
342             Returns a HASH reference of all the properties in the given property group.
343              
344             =head2 property
345              
346             $value = $avr->property( $group_name, $prop_name )
347              
348             Returns a single value of a property in the given property group.
349              
350             Any value given in the XML file in the form of a single hexadecimal numerical
351             constant is automatically converted into the appropriate integer. Strings of
352             multiple numbers (such as the HVSP and HVPP control stacks) are not converted.
353              
354             =cut
355              
356             sub property_groups
357             {
358 0     0 1   my $self = shift;
359             $self->{property_groups} //= { map {
360             +( "$_->{name}", $_->{property} )
361 0   0       } @{ $self->{_device}{"property-groups"}{"property-group"} } };
  0            
  0            
362 0           return keys %{ $self->{property_groups} };
  0            
363             }
364              
365             sub property_group
366             {
367 0     0 1   my $self = shift;
368 0           my ( $group ) = @_;
369              
370 0           $self->property_groups;
371 0 0         my $properties = $self->{property_groups}{$group} or
372             croak "No such property group '$group'";
373              
374             return $self->{properties}{$group} //= { map {
375 0   0       my $value = $_->{value};
  0            
376 0 0         $value = hex $value if $value =~ m/^0x[[:xdigit:]]+$/;
377              
378 0           +( "$_->{name}", "$value" )
379             } @$properties };
380             }
381              
382             sub property
383             {
384 0     0 1   my $self = shift;
385 0           my ( $group, $prop ) = @_;
386              
387 0           return $self->property_group( $group )->{$prop};
388             }
389              
390             =head1 DERIVED METHODS
391              
392             These methods wrap information provided by the basic accessors.
393              
394             =cut
395              
396             =head2 signature
397              
398             $sig = $avr->signature
399              
400             Returns a 6-character hexadecimal string consisting of the three bytes of the
401             device signature.
402              
403             =cut
404              
405             sub signature
406             {
407 0     0 1   my $self = shift;
408             return sprintf "%02x%02x%02x",
409 0           map { $self->property( SIGNATURES => "SIGNATURE$_" ) } 0 .. 2;
  0            
410             }
411              
412             =head1 AUTHOR
413              
414             Paul Evans
415              
416             =cut
417              
418             0x55AA;