Device-AVR-UPDI
view release on metacpan or search on metacpan
lib/Device/AVR/UPDI.pm view on Meta::CPAN
=cut
async method write_eeprom_page ( $addr, $data )
{
await $self->nvmctrl->write_eeprom_page( $addr, $data );
}
=head2 write_fuse
await $updi->write_fuse( $idx, $value );
Writes a fuse value. C<$idx> is the index of the fuse within the FUSES memory
segment, from 0 onwards.
=cut
async method write_fuse ( $idx, $value )
{
await $self->nvmctrl->write_fuse( $idx, $value );
}
=head2 read_fuse
$value = await $updi->read_fuse( $idx );
Reads a fuse value. C<$idx> is the index of the fuse within the FUSES memory
segment, from 0 onwards.
=cut
async method read_fuse ( $idx )
{
my $addr = $_partinfo->baseaddr_fuse + $idx;
return await $self->lds8( $addr );
}
class # hide from indexer
Device::AVR::UPDI::_NVMCtrl {
use constant {
NVMCTRL_CTRLA => 0,
NVMCTRL_STATUS => 2,
NVMCTRL_STATUS_FBUSY => (1<<0),
};
field $_updi :param :inheritable;
field $_partinfo :inheritable;
ADJUST
{
$_partinfo = $_updi->partinfo;
}
async method nvmctrl_command ( $cmd )
{
await $_updi->sts8( $_partinfo->baseaddr_nvmctrl + NVMCTRL_CTRLA, $cmd );
}
async method await_nvm_not_busy ()
{
my $timeout = 50;
while( --$timeout ) {
last if not( NVMCTRL_STATUS_FBUSY & await $_updi->lds8(
$_partinfo->baseaddr_nvmctrl + NVMCTRL_STATUS ) );
await Future::IO->sleep( 0.01 );
}
}
}
class # hide from indexer
Device::AVR::UPDI::_NVMCtrlv0 {
inherit Device::AVR::UPDI::_NVMCtrl qw( $_updi $_partinfo );
use Carp;
use constant {
# Command values
NVMCTRL_CMD_WP => 1,
NVMCTRL_CMD_ER => 2,
NVMCTRL_CMD_ERWP => 3,
NVMCTRL_CMD_PBC => 4,
NVMCTRL_CMD_CHER => 5,
NVMCTRL_CMD_EEER => 6,
NVMCTRL_CMD_WFU => 7,
NVMCTRL_CTRLB => 1,
NVMCTRL_DATA => 6,
NVMCTRL_ADDR => 8,
};
async method read_flash_page ( $addr, $len )
{
return await $_updi->ld( $_partinfo->baseaddr_flash + $addr, $len );
}
async method read_eeprom_page ( $addr, $len )
{
return await $_updi->ld( $_partinfo->baseaddr_eeprom + $addr, $len );
}
async method _write_page ( $addr, $data, $wordsize, $cmd )
{
# clear page buffer
await $self->nvmctrl_command( NVMCTRL_CMD_PBC );
await $self->await_nvm_not_busy;
# Disable response sig for speed
await $_updi->set_rsd( 1 );
if( $wordsize == 8 ) {
await $_updi->st8( $addr, $data );
}
elsif( $wordsize == 16 ) {
await $_updi->st16( $addr, $data );
}
else {
croak "Invalid word size";
}
# Re-enable response sig again
await $_updi->set_rsd( 0 );
await $self->nvmctrl_command( $cmd );
await $self->await_nvm_not_busy;
}
async method write_flash_page ( $addr, $data )
{
await $self->_write_page( $_partinfo->baseaddr_flash + $addr, $data, 16, NVMCTRL_CMD_WP );
}
async method write_eeprom_page ( $addr, $data )
{
await $self->_write_page( $_partinfo->baseaddr_eeprom + $addr, $data, 8, NVMCTRL_CMD_ERWP );
}
async method write_fuse ( $idx, $value )
{
my $addr = $_partinfo->baseaddr_fuse + $idx;
my $baseaddr = $_partinfo->baseaddr_nvmctrl;
# Oddly, this works but an attempt at STS16 does not. Unsure why
await $_updi->sts8 ( $baseaddr + NVMCTRL_ADDR , $addr & 0xFF );
await $_updi->sts8 ( $baseaddr + NVMCTRL_ADDR+1, $addr >> 8 );
await $_updi->sts8 ( $baseaddr + NVMCTRL_DATA, $value );
await $self->nvmctrl_command( NVMCTRL_CMD_WFU );
await $self->await_nvm_not_busy;
}
}
class # hide from indexer
Device::AVR::UPDI::_NVMCtrlv2 {
inherit Device::AVR::UPDI::_NVMCtrl qw( $_updi $_partinfo );
use Carp;
use constant {
# Command values
NVMCTRL_CMD_NOCMD => 0x00,
NVMCTRL_CMD_FLWR => 0x02,
NVMCTRL_CMD_EEERWR => 0x13,
NVMCTRL_CTRLB => 1,
};
async method _set_flmap ( $bank )
{
await $_updi->sts8( $_partinfo->baseaddr_nvmctrl + NVMCTRL_CTRLB, $bank << 4 );
}
async method read_flash_page ( $addr, $len )
{
await $self->_set_flmap( $addr >> 15 );
$addr &= 0x7FFF;
return await $_updi->ld( $_partinfo->baseaddr_flash + $addr, $len );
}
async method read_eeprom_page ( $addr, $len )
{
return await $_updi->ld( $_partinfo->baseaddr_eeprom + $addr, $len );
}
async method _write_page ( $addr, $data, $wordsize, $cmd )
{
# set page write mode
await $self->nvmctrl_command( $cmd );
# Disable response sig for speed on long data
# (no point on single-byte fuses)
await $_updi->set_rsd( 1 ) if length $data > 1;
if( $wordsize == 8 ) {
await $_updi->st8( $addr, $data );
}
elsif( $wordsize == 16 ) {
await $_updi->st16( $addr, $data );
}
else {
croak "Invalid word size";
}
# Re-enable response sig again
await $_updi->set_rsd( 0 );
await $self->await_nvm_not_busy;
# clear command
await $self->nvmctrl_command( NVMCTRL_CMD_NOCMD );
await $self->await_nvm_not_busy;
}
async method write_flash_page ( $addr, $data )
{
await $self->_set_flmap( $addr >> 15 );
$addr &= 0x7FFF;
await $self->_write_page( $_partinfo->baseaddr_flash + $addr, $data, 16, NVMCTRL_CMD_FLWR );
}
async method write_eeprom_page ( $addr, $data )
{
await $self->_write_page( $_partinfo->baseaddr_eeprom + $addr, $data, 8, NVMCTRL_CMD_EEERWR );
}
async method write_fuse ( $idx, $value )
{
# Fuses are written by pretending it's EEPROM
my $data = pack "C", $value;
await $self->_write_page( $_partinfo->baseaddr_fuse + $idx, $data, 8, NVMCTRL_CMD_EEERWR );
}
}
=head1 SEE ALSO
=over 2
=item *
"AVR UPDI Programming Cable"
An adapter cable to flash firmware onto an AVR microcontroller chip via UPDI,
compatible with this module.
L<https://www.tindie.com/products/16571/>
=back
=cut
=head1 AUTHOR
Paul Evans <leonerd@leonerd.org.uk>
=cut
0x55AA;
__DATA__
# These data are maintained by rebuild-partinfo.pl
# name|signature|baseaddr_nvmctl|baseaddr_fuse|baseaddr_sigrow|baseaddr_flash|pagesize_flash|size_flash|baseaddr_eeprom|pagesize_eeprom|size_eeprom|fuses
ATmega808|1e9326|0x1000|0x1280|0x1100|0x4000|64|8192|0x1400|32|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega809|1e932a|0x1000|0x1280|0x1100|0x4000|64|8192|0x1400|32|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega1608|1e9427|0x1000|0x1280|0x1100|0x4000|64|16384|0x1400|32|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega1609|1e9426|0x1000|0x1280|0x1100|0x4000|64|16384|0x1400|32|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega3208|1e9530|0x1000|0x1280|0x1100|0x4000|128|32768|0x1400|64|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega3209|1e9531|0x1000|0x1280|0x1100|0x4000|128|32768|0x1400|64|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega4808|1e9650|0x1000|0x1280|0x1100|0x4000|128|49152|0x1400|64|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
ATmega4809|1e9651|0x1000|0x1280|0x1100|0x4000|128|49152|0x1400|64|256|WDTCFG,BODCFG,OSCCFG,,,SYSCFG0,SYSCFG1,APPEND,BOOTEND
( run in 1.736 second using v1.01-cache-2.11-cpan-39bf76dae61 )