XMEGA Self-Programming Flash Range CRC Calculation

Go To Last Post
32 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear all,

I am attempting to implement the "Flash Range CRC" Feature as described in the xmega reference manual (page p370) for the ATxmega128A1.

Unfortunantly, the self-programming application note/driver (AVR1316) does not implement this particular function (indeed, does not even mention it in the PDF).

In theory (according to my logic), if the device is supplied with the same start and end addressess as the flash application, it should result in precisely the same calculation as the normal SP_ApplicationCRC and SP_BootCRC as supplied in the above application note.

I have coded a simple test harness to test this out:

#include "sp_driver.h"
#include 
#include 
#include 

// Target is the ATxmega128A1
#define APPLICATION_START_BYTE_ADDRESS  0x000000L
// #define APPLICATION_END_BYTE_ADDRESS    0x01FFFFL
#define APPLICATION_END_BYTE_ADDRESS    0x020000L

#define BOOTLOADER_START_BYTE_ADDRESS   0x020000L 
// #define BOOTLOADER_END_BYTE_ADDRESS     0x021FFFL 
#define BOOTLOADER_END_BYTE_ADDRESS     0x022000L 

#define LED_PASS_PORT       PORTB
#define LED_PASS_PIN_MASK   (1 << 0)

#define LED_FAIL_PORT       PORTB
#define LED_FAIL_PIN_MASK   (1 << 1)

int main(void)
{
    // Setup the LEDs
    LED_PASS_PORT.OUTSET = LED_PASS_PIN_MASK;
    LED_PASS_PORT.DIRSET = LED_PASS_PIN_MASK;

    LED_FAIL_PORT.OUTSET = LED_FAIL_PIN_MASK;
    LED_FAIL_PORT.DIRSET = LED_FAIL_PIN_MASK;

    // Calculate the CRC of the application section
    uint32_t applicationCRC = SP_ApplicationCRC();
    SP_WaitForSPM();

    // Calculate the CRC of the boot section
    uint32_t bootloaderCRC = SP_BootCRC();
    SP_WaitForSPM();

    // Calculate the CRC of the application section using the CRC Range command
    uint32_t applicationCRC_Range = SP_FlashRangeCRC(APPLICATION_START_BYTE_ADDRESS, APPLICATION_END_BYTE_ADDRESS);

    // Calculate the CRC of the bootloader section using the CRC Range command
    uint32_t bootloaderCRC_Range = SP_FlashRangeCRC(BOOTLOADER_START_BYTE_ADDRESS, BOOTLOADER_END_BYTE_ADDRESS);

    // Check the results - the CRC calculations should match
    bool ok = true;
    ok = ok && (bootloaderCRC  == bootloaderCRC_Range);
    ok = ok && (applicationCRC == applicationCRC_Range); 

    // Give the result to the user
    if (ok)
    {
        LED_PASS_PORT.OUTCLR = LED_PASS_PIN_MASK;
    }
    else
    {
        LED_FAIL_PORT.OUTCLR = LED_FAIL_PIN_MASK;
    }

    while(1) {}
}

With the custom part of the sp_driver.S (GCC Assembler):


;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R25:R24:R23:R22 - Starting address for CRC, in bytes
;     R21:R20:R19:R18 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text	
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; We're doing this after loading the registers to take advantage of common code
    ; Load the NVM CMD Register with the flash range CRC command
    ; ldi     r26, NVM_CMD_FLASH_RANGE_CRC_gc
    ; sts     NVM_CMD, r26

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r22
    sts     NVM_ADDR1, r23
    sts     NVM_ADDR2, r24

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r18
    sts     NVM_DATA1, r19
    sts     NVM_DATA2, r20

    ; Command the sequence  
	ldi	    r20, NVM_CMD_FLASH_RANGE_CRC_gc     ; Prepare NVM command in R20.
	rjmp	SP_CommonCMD                        ; Jump to common NVM Action code.

EDIT: Adding the common SP code below

; ---
; This routine is called by several other routines, and contains common code
; for executing an NVM command, including the return statement itself.
;
; If the operation (NVM command) requires the NVM Address registers to be
; prepared, this must be done before jumping to this routine.
;
; Note that R25:R24:R23:R22 is used for returning results, even if the
; C-domain calling function only expects a single byte or even void.
;
; Input:
;     R20 - NVM Command code.
;
; Returns:
;     R25:R24:R23:R22 - 32-bit result from NVM operation.
; ---

.section .text		

SP_CommonCMD:
	sts	NVM_CMD, r20        ; Load command into NVM Command register.
	ldi	r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
	ldi	r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
	sts	CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
	sts	NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
	lds	r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
	lds	r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
	lds	r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
	clr	r25                 ; Clear R25 in order to return a clean 32-bit value.
	ret

EDIT 2: And the function protoype as follows:

/*! \brief Generate CRC for a given address range
 *
 *  \param startAddress is the starting byte address of the range to perform the CRC
 *  \param endAddress is the ending byte address of the range to perform the CRC
 *
 *  \retval 24-bit CRC value
 */
uint32_t SP_FlashRangeCRC(uint32_t startAddress, uint32_t endAddress);

I have included the entire project for reference below.

If anyone is able to suggest any pointers as to why this is misbehaving, I would be very much grateful. At the moment, I'm at a bit stuck on it.

-- Damien

Attachment(s): 

Last Edited: Thu. Jun 4, 2009 - 01:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try giving the NVM controller a NVM_CMD_NO_OPERATION_gc (0x00)
command after each function call into sp_driver.S and see if that changes
anything for you.

There seems to be something not quite right about sp_driver.S,
but I've not put my figure on exactly what. I have noted that
my program goes off into the weeds when trying to read
calibration bytes using sp_driver.S, if I don't do a NVM no-op command, while this
code:

uint8_t SP_ReadCalibrationByteC( uint8_t index )
{
uint8_t result;

/* Load the NVM Command register to read the calibration row. */
NVM_CMD = NVM_CMD_READ_CALIB_ROW_gc;
result = pgm_read_byte(index);

/* Clean up NVM Command register. */
NVM_CMD = NVM_CMD_NO_OPERATION_gc;

return( result );
}

always seems to work fine.

Something else I've found confusing is that the manual talks of
the "Production Signature Row":

"The Production Signature Row is a separate memory section for factory programmed data. It
contains calibration data for functions such as oscillators and analog modules..."

which the data sheet says is protected by the CCP, see section 3.12:

"the LPM instruction is protected when reading the fuses and signature row."

yet the software talks about the calibration row and does not use CCP.
In iox128a1 the calibration constants are in the production signature row,
see NVM_PROD_SIGNATURES_struct. So which is which, or is the manual wrong?

The following snippet does what I expect when I use my C function,
but not when using sp_driver.S.

/* Device serial number: */

hex8( SP_ReadCalibrationByteC( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM1 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM2 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM3 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM4 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, LOTNUM5 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, WAFNUM ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, COORDX0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, COORDX1 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, COORDY0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, COORDY1 ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, TEMPSENSE0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, TEMPSENSE1 ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, ADCACAL0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, ADCACAL1 ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, ADCBCAL0 ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, ADCBCAL1 ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, DACAOFFCAL ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, DACACAINCAL ) ) );

hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, DACBOFFCAL ) ) );
hex8( SP_ReadCalibrationByte( offsetof( NVM_PROD_SIGNATURES_t, DACBGAINCAL ) ) );

[offsetof() is in stddef.h., hex8 displays a hex byte on the units lcd screen.]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:
Try giving the NVM controller a NVM_CMD_NO_OPERATION_gc (0x00)
command after each function call into sp_driver.S and see if that changes
anything for you.

There seems to be something not quite right about sp_driver.S,
but I've not put my figure on exactly what. I have noted that
my program goes off into the weeds when trying to read
calibration bytes using sp_driver.S, if I don't do a NVM no-op command

Unfortunantly no joy in just adding a nop to the end:

SP_CommonCMD:
	sts	NVM_CMD, r20        ; Load command into NVM Command register.
	ldi	r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
	ldi	r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
	sts	CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
	sts	NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.

    ; Transfer the return result
	lds	r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
	lds	r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
	lds	r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
	clr	r25                 ; Clear R25 in order to return a clean 32-bit value.

    ; HACK: Adding nop to test for suggestion in thread
    ; NVM_CMD = NVM_CMD_NO_OPERATION_gc; 
    ldi r25, NVM_CMD_NO_OPERATION_gc
    sts NVM_CMD, r25
    clr r25

	ret

(compare with the common routine).

I'm not confident with the assembly that I've written (Have I got the registers right for the gcc calling convention?). I'm wondering if I even have the parmeters correct in the first place :(

The watch variables below may offer a clue to someone... I wonder what's significant about the 0x0056xxxx ??

Sometime today, I'll add your results to my test harness and see what happens.

Attachment(s): 

Last Edited: Thu. Jun 4, 2009 - 01:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you have the parameters backwards. IIRC IAR starts with the first parameter at r16 and works up from there.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
I think you have the parameters backwards. IIRC IAR starts with the first parameter at r16 and works up from there.

I'm using gcc rather than IAR.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yeah sorry, was just realizing that it may not be IAR, and was going to delete. Not sure why I thought it was IAR.

Move along, nothing to see here ;)

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
yeah sorry, was just realizing that it may not be IAR, and was going to delete. Not sure why I thought it was IAR.

No worries, I appreciate that you're looking :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:

The return value from SP_CommonCMD is also at R16-R19

According to the GCC calling conventions at http://www.nongnu.org/avr-libc/u..., the return registers are a little different (and hence, my original code):

Quote:

* Function call conventions:
Arguments - allocated left to right, r25 to r8. All arguments are aligned to start in even-numbered registers (odd-sized arguments, including char, have one free register above them). This allows making better use of the movw instruction on the enhanced core.

If too many, those that don't fit are passed on the stack.

Return values: 8-bit in r24 (not r25!), 16-bit in r25:r24, up to 32 bits in r22-r25, up to 64 bits in r18-r25. 8-bit return values are zero/sign-extended to 16 bits by the called function (unsigned char is more efficient than signed char - just clr r25). Arguments to functions with variable argument lists (printf etc.) are all passed on stack, and char is extended to int.

A question to the asm/gcc gurus, I've noticed that my return values share the same registers as some of the arguments to the function. How does the compiler get around this?

In either case, I tried your suggestion as follows, but with no luck:


;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R19:R18:R17:R16 - Starting address for CRC, in bytes
;     R23:R22:R21:R20 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R19:R18:R17:R16 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text   
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r16
    sts     NVM_ADDR1, r17
    sts     NVM_ADDR2, r18

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r20
    sts     NVM_DATA1, r21
    sts     NVM_DATA2, r22

    ; Command the sequence
    ldi     r20, NVM_CMD_FLASH_RANGE_CRC_gc     ; Prepare NVM command in R20.

    ; Common code reimplemented here
  	sts	NVM_CMD, r20        ; Load command into NVM Command register.
	ldi	r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
	ldi	r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
	sts	CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
	sts	NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.

    ; Transfer the return result
	lds	r16, NVM_DATA0      ; Load NVM Data Register 0 into R22.
	lds	r17, NVM_DATA1      ; Load NVM Data Register 1 into R23.
	lds	r18, NVM_DATA2      ; Load NVM Data Register 2 into R24.
	clr	r19                 ; Clear R25 in order to return a clean 32-bit value.
	ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, for GCC it looks like they allocate starting at R25, and work down, so all you had was the start and end backwards. I also didn't like the double loading of the NVM_CMD register, so I replaced the jump to the code, with the remaining relevant code.

Sorry, don't have a xmega to try with.. so give this a go

;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R21:R20:R19:R18 - Starting address for CRC, in bytes
;     R25:R24:R23:R22 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text   
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ; Command the sequence 
	ldi	r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
	ldi	r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
	sts	CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
	sts	NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
	lds	r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
	lds	r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
	lds	r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
	clr	r25                 ; Clear R25 in order to return a clean 32-bit value.
	ret

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

Last Edited: Thu. Jun 4, 2009 - 02:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

damien_d wrote:
glitch wrote:

The return value from SP_CommonCMD is also at R16-R19

According to the GCC calling conventions at http://www.nongnu.org/avr-libc/u..., the return registers are a little different (and hence, my original code):

yeah you caught me in an edit... I posted and then realized I grabbed the IAR version, not the GCC version. See above for my corrected code. Sorry again for the confusion.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just for completeness... this is the equivalent IAR version: (I deleted my previous post/edit hoping to avoid further confusion, but got quoted, so here is my final version)

;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R19:R18:R17:R16 - Starting address for CRC, in bytes
;     R23:R22:R21:R20 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R19:R18:R17:R16 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
MODULE _SP_CommonSPM
PUBLIC SP_CommonSPM
RSEG BOOT
#include 		

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r23, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r23

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r16
    sts     NVM_ADDR1, r17
    sts     NVM_ADDR2, r18

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r20
    sts     NVM_DATA1, r21
    sts     NVM_DATA2, r22

    ; Command the sequence
Prepare NVM command in R20.
   ldi   r16, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R16.
   ldi   r17, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R17.
   sts   CCP, r16            ; Enable IO-register operation (this disables interrupts for 4 cycles).
   sts   NVM_CTRLA, r17      ; Load bitmask into NVM Control Register A, which executes the command.
   lds   r16, NVM_DATA0      ; Load NVM Data Register 0 into R16.
   lds   r17, NVM_DATA1      ; Load NVM Data Register 1 into R17.
   lds   r18, NVM_DATA2      ; Load NVM Data Register 2 into R18.
   clr   r19                 ; Clear R19 in order to return a clean 32-bit value.
   ret 
ENDMOD

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

Last Edited: Thu. Jun 4, 2009 - 02:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:

Sorry, don't have a xmega to try with.. so give this a go

Just implemented. No joy, sorry.

Interestingly, both calculated ranges return 0x00560000.

(As Implemented above for GCC)


;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R21:R20:R19:R18 - Starting address for CRC, in bytes
;     R25:R24:R23:R22 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text   
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ; Command the sequence
   ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
   ldi   r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
   sts   CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
   sts   NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
   lds   r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
   lds   r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
   lds   r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
   clr   r25                 ; Clear R25 in order to return a clean 32-bit value.
   ret 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

umm, where did the initial load of NVM_CMD go?

The function should start like:

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r26, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r26

    ; Load the starting address in the NVM Address register

What I removed was the 2nd loading of NVM_CMD, which was happening in SP_CommonCMD

[edit]

on further reflection that should be using R25, to avoid any un-intended side-effects

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25

    ; Load the starting address in the NVM Address register

I'll go back and update my codes above
[/edit]

[edit 2]
Ah, I see now, it was originally commented out and I didn't catch that... needs to be uncommented. I've adjusted all the code above accordingly
[/edit]

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah yes, I have fixed it, but no luck yet. I have tried both loading the NVM control before and after loading the individual addresses:

.section .text   
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25 

    ; Command the sequence
    ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
    ldi   r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
    sts   CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
    sts   NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
    lds   r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
    lds   r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
    lds   r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
    clr   r25                 ; Clear R25 in order to return a clean 32-bit value.
    ret 

And again with loading the command before setting off:

.section .text   
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25 

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ; Command the sequence
    ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
    ldi   r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
    sts   CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
    sts   NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
    lds   r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
    lds   r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
    lds   r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
    clr   r25                 ; Clear R25 in order to return a clean 32-bit value.
    ret 

Both yield the magic 0x00560000 value we've seen before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

damien_d wrote:
Ah yes, I have fixed it, but no luck yet. I have tried both loading the NVM control before and after loading the individual addresses:

    ; Command the sequence
    ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.

What do you get if you replace CPP_IOREG_gc
with CCP_SPM_gc? To me it seems logical that the
CRC hardware would do something like a LPM access
to read the flah bytes. See section 3.12.2 in the manual.

I'll see what CRC values I get out of my xmega.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried several variations of the followin code,
such as adding NO_OPERATION, setting the address/data before
and after the command, even both etc:

;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32_t startAddress, uint32_t endAddress);
;
; Input:
;     R21:R20:R19:R18 - Starting address for CRC, in bytes
;     R25:R24:R23:R22 - Ending address for CRC, in bytes. CRC includes this address
;
; Returns:
;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bit used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text
.global SP_FlashRangeCRC

SP_FlashRangeCRC:
    ldi    r25, NVM_CMD_NO_OPERATION_gc
    sts    NVM_CMD, r25

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r18
    sts     NVM_ADDR1, r19
    sts     NVM_ADDR2, r20

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r22
    sts     NVM_DATA1, r23
    sts     NVM_DATA2, r24

    ; Command the sequence
    ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
    ldi   r18, 0x9D
    ldi   r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
    sts   CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
    sts   NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
    lds   r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
    lds   r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
    lds   r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.

    ldi    r25, NVM_CMD_NO_OPERATION_gc
    sts    NVM_CMD, r25

    ; clr   r25                 ; Clear R25 in order to return a clean 32-bit value.
    ldi r25,0x42                ; Show the code ran
    ret

The most significant thing I found was that the value written to
CCP makes no difference *at all*, even writing 0x00 to CPP, in the
results I see using this code:

// Target is the ATxmega128A1
#define APPLICATION_START_BYTE_ADDRESS  0x000000L
#define APPLICATION_END_BYTE_ADDRESS    0x01FFFFL
  //#define APPLICATION_END_BYTE_ADDRESS    0x020000L

#define BOOTLOADER_START_BYTE_ADDRESS   0x020000L
#define BOOTLOADER_END_BYTE_ADDRESS     0x021FFFL
//#define BOOTLOADER_END_BYTE_ADDRESS     0x022000L
/* hex32() displays a 32 bit hex number on the units LCD */

  buf_ptr_u8 = buffer_u8;
  SP_WaitForSPM(); hex32( SP_ApplicationCRC() ); *buf_ptr_u8++= (uint8_t) ' ';
  SP_WaitForSPM(); hex32( SP_FlashRangeCRC(APPLICATION_START_BYTE_ADDRESS, APPLICATION_END_BYTE_ADDRESS) ); *buf_ptr_u8++= (uint8_t) ' ';
  *buf_ptr_u8 = 0;
  lcd_text(SCRN_LEFT+0,  SCRN_TOP+12, FONT_FIVE_DOT, (char *) buffer_u8);

  buf_ptr_u8 = buffer_u8;
  SP_WaitForSPM(); hex32( SP_BootCRC() );        *buf_ptr_u8++= (uint8_t) ' ';
  SP_WaitForSPM(); hex32( SP_FlashRangeCRC(BOOTLOADER_START_BYTE_ADDRESS, BOOTLOADER_END_BYTE_ADDRESS) );   *buf_ptr_u8++= (uint8_t) ' ';
  hex32( 0x12345678 );
  *buf_ptr_u8 = 0;
  lcd_text(SCRN_LEFT+0,  SCRN_TOP+18, FONT_FIVE_DOT, (char *) buffer_u8);
  lcd_update(SCRN_TOP,SCRN_BOTTOM);

The values I always got are along the lines of:

00989DCC 00980000
00261F77 00260000

The bottom 16 bits of FlashRange always returned zero, while the upper 16 bits
always matched, and varied with each compile.

Does anyone have C code that returns the same CRC results as the xmega hardware results?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As a test, try loading the fixed values, in the assembly code itself, into the registers to see if it makes any difference. This should tell us if we are receiving the parameters correctly or not. (though looking at the other functions in the driver, it should be correct)

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

As a test, try loading the fixed values, in the assembly code itself, into the registers to see if it makes any difference. This should tell us if we are receiving the parameters correctly or not. (though looking at the other functions in the driver, it should be correct)

Made no difference. I tried reversing the start/end which did change the result,
the lower 16 bits now always have 0xFFFF rather than 0x0000.

I think it is more telling of a hardware problem that the value written
to CCP doesn't make any difference. Is there any official Atmel
code, known to work, that shows how to use FlashRange?

; Use one of the following sets:
     	ldi	r22, 0x00	; 0
      	ldi	r23, 0x00	; 0
     	ldi	r24, 0x00	; 0
     	ldi	r25, 0x00	; 0

     	ldi	r18, 0xFF	; 255
      	ldi	r19, 0xFF	; 255
    	ldi	r20, 0x01	; 1
     	ldi	r21, 0x00	; 0

        ; end/start:
       	ldi	r18, 0x00	; 0
     	ldi	r19, 0x00	; 0
     	ldi	r20, 0x00	; 0
     	ldi	r21, 0x00	; 0

      	ldi	r22, 0xFF	; 255
      	ldi	r23, 0xFF	; 255
     	ldi	r24, 0x01	; 1
     	ldi	r25, 0x00	; 0
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, now I'm starting to read all the docs instead of working off of the provided code.

Question: What are your boot lock bits programmed as?

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

They are not programmed, per footnote #3.
I have read the docs. They are inconsistent in places.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
Question: What are your boot lock bits programmed as?

Sorry, for the long time between replies. All lock bit are unprogrammed, as per the requirements for calling the function.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:
They are not programmed, per footnote #3.
I have read the docs. They are inconsistent in places.

For clarity, This refers to the following footnote for the FLASH_RANGE_CRC command:

Quote:

3. This command is qualified with the Lock BIts, and requires that the Boot Lock Bits are unprogrammed

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

damien_d wrote:
Dear all,

I am attempting to implement the "Flash Range CRC" Feature as described in the xmega reference manual (page p370) for the ATxmega128A1.

Give it up, it is officially broken, actually doesn't exist a all
in the ATxmega128A1 RevH:

From avr(at)atmel.com:

"The 'Flash Range CRC' functionality is an added feature
currently present in the latest xmega A3 and A4 series. However
this is intended to be added to A1 in revision J.

This information is currently missing from the documentation,
but will be added for the next documentation release."
-- Best Regards, Jon Anders Haugum, Atmel Technical Support Team

So what else in Xmega land can we waste valuable time on that is not documented? :-(

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:

Give it up, it is officially broken, actually doesn't exist a all in the ATxmega128A1 RevH:

Dear Admins,

What language is acceptable for use on these forums? I'd like to use some words that my mother would frown at.

At least we now know. I'll link this thread to the "Gotchas" sticky.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:
damien_d wrote:
Dear all,

I am attempting to implement the "Flash Range CRC" Feature as described in the xmega reference manual (page p370) for the ATxmega128A1.

Give it up, it is officially broken, actually doesn't exist a all
in the ATxmega128A1 RevH:

From avr(at)atmel.com:

"The 'Flash Range CRC' functionality is an added feature
currently present in the latest xmega A3 and A4 series. However
this is intended to be added to A1 in revision J.

This information is currently missing from the documentation,
but will be added for the next documentation release."
-- Best Regards, Jon Anders Haugum, Atmel Technical Support Team

So what else in Xmega land can we waste valuable time on that is not documented? :-(

Is this still the current state of things? I had a lot of problems getting this to work on RevH, but I think I am getting reasonable 24-bit values for both flash and bootloader sections. One thing I found that would prevent some kind of PC corruption is putting a SP_WaitForSPM() after each NVM command that used LVM. I know that isn't right and might just serve a small delay. I haven't verified that. I also had to do the SP_WaitForSPM() for reading/writing User Sig Row, Production Row. Those both verify with what AVR Studio reports.

Here is sample crcs, bootloader is always the same, and application only changes when I update it.

application crc: 0x0054b9cc
bootloader crc : 0x00cd2554

Is the problem just that it can't be verified against the algorithm or is everyone still having problems getting 24-bit values from the commands? I can provide source if anybody is interested.

I also found a related error in A manual and avr-libc,
see here
https://www.avrfreaks.net/index.p...

[edit]
I just realized that I implied that the crc commands were LPM commands. They aren't. They are NVM CMD functions. I also just checked and I don't need the SP_WaitForSPM() after each CRC CMD to make it work correctly. I only had to do that after the LPM Commands like User Sig R/W, and Production Row.

[edit2] Here is another nice 24-bit crc after application update
application crc: 0x00489168
bootloader crc : 0x00cd2554

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:

Give it up, it is officially broken, actually doesn't exist a all in the ATxmega128A1 RevH:

Is it possible that someone made an error in their reply from Atmel? I am getting random-looking, but consistent 24-bit crc everytime I update the application on a Rev H Xmega128A1.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

telliott wrote:
bpaddock wrote:

Give it up, it is officially broken, actually doesn't exist a all in the ATxmega128A1 RevH:

Is it possible that someone made an error in their reply from Atmel? I am getting random-looking, but consistent 24-bit crc everytime I update the application on a Rev H Xmega128A1.

Are you speaking of the CRC command(s) (0x38/0x39),
or the CRC Range command (0x3A)? CRC works fine.
It is the CRC Range command that is broken.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bpaddock wrote:

Are you speaking of the CRC command(s) (0x38/0x39),
or the CRC Range command (0x3A)? CRC works fine.
It is the CRC Range command that is broken.

Sorry Bob, I was confused on that. Thanks for clearing that up.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Success on a ATxmega32A4!


#include "sp_driver.h"
#include 
#include 
#include 

#include 
#include 
#include "usart_driver.h"

/*
// Target is the ATxmega128A1
#define APPLICATION_START_BYTE_ADDRESS  (0x000000L    )
#define APPLICATION_START_WORD_ADDRESS  (APPLICATION_START_BYTE_ADDRESS >> 1)
#define APPLICATION_END_BYTE_ADDRESS    (0x020000L - 1L)                         // End in bytes
#define APPLICATION_END_WORD_ADDRESS    (APPLICATION_END_BYTE_ADDRESS >> 1)     // End in words

#define BOOTLOADER_START_BYTE_ADDRESS   0x020000L 
#define BOOTLOADER_START_WORD_ADDRESS   (BOOTLOADER_START_BYTE_ADDRESS >> 1)
#define BOOTLOADER_END_BYTE_ADDRESS     (0x022000L - 1L)
#define BOOTLOADER_END_WORD_ADDRESS     (BOOTLOADER_END_BYTE_ADDRESS >> 1)
*/

// Target is the ATxmega32A4
#define APPLICATION_START_BYTE_ADDRESS  (0x000000L    )
#define APPLICATION_START_WORD_ADDRESS  (APPLICATION_START_BYTE_ADDRESS >> 1)
#define APPLICATION_END_BYTE_ADDRESS    (0x0008000L - 1L)                         // End in bytes
#define APPLICATION_END_WORD_ADDRESS    (APPLICATION_END_BYTE_ADDRESS >> 1)     // End in words

#define BOOTLOADER_START_BYTE_ADDRESS   0x008000L 
#define BOOTLOADER_START_WORD_ADDRESS   (BOOTLOADER_START_BYTE_ADDRESS >> 1)
#define BOOTLOADER_END_BYTE_ADDRESS     (0x09000L - 1L)
#define BOOTLOADER_END_WORD_ADDRESS     (BOOTLOADER_END_BYTE_ADDRESS >> 1)

// LED Pin Masks
#define LED_PASS_PORT       PORTB
#define LED_PASS_PIN_MASK   (1 << 0)

#define LED_FAIL_PORT       PORTB
#define LED_FAIL_PIN_MASK   (1 << 1)

// Global access for Serial Transmission
uint32_t applicationCRC;
uint32_t bootloaderCRC;

uint32_t applicationCRC_Range;
uint32_t bootloaderCRC_Range;

// Serial Buffers
char bootSerialBuffer[128];
char applicationSerialBuffer[128];

// Function prototypes
void UART_Setup();
void UART_SendString(char* buffer, int length);


bool TestHarness_FlashRangeCRC()
{
    // Calculate the CRC of the application section
    applicationCRC = SP_ApplicationCRC();
    SP_WaitForSPM();

    // Calculate the CRC of the boot section
    bootloaderCRC = SP_BootCRC();
    SP_WaitForSPM();

    // Calculate the CRC of the application section using the CRC Range command
    applicationCRC_Range = SP_FlashRangeCRC(APPLICATION_START_BYTE_ADDRESS, APPLICATION_END_BYTE_ADDRESS);
    // applicationCRC_Range = SP_FlashRangeCRC(APPLICATION_END_BYTE_ADDRESS, APPLICATION_START_BYTE_ADDRESS);

    // Calculate the CRC of the bootloader section using the CRC Range command
    bootloaderCRC_Range = SP_FlashRangeCRC(BOOTLOADER_START_BYTE_ADDRESS, BOOTLOADER_END_BYTE_ADDRESS);

    // Check the results - the CRC calculations should match
    bool ok = true;
    ok = ok && (bootloaderCRC  == bootloaderCRC_Range);
    ok = ok && (applicationCRC == applicationCRC_Range); 

    return ok;
}


int main(void)
{
    // Setup the LEDs
    LED_PASS_PORT.OUTSET = LED_PASS_PIN_MASK;
    LED_PASS_PORT.DIRSET = LED_PASS_PIN_MASK;

    LED_FAIL_PORT.OUTSET = LED_FAIL_PIN_MASK;
    LED_FAIL_PORT.DIRSET = LED_FAIL_PIN_MASK;

    bool ok = TestHarness_FlashRangeCRC();

    // Create the results to send out the serial
    sprintf(applicationSerialBuffer, "Application: Range = 0x%08lX, Calc = 0x%08lX\r\n",     applicationCRC_Range, applicationCRC);
    sprintf(bootSerialBuffer,        "Boot:        Range = 0x%08lX, Calc = 0x%08lX\r\n\r\n", bootloaderCRC_Range, bootloaderCRC);

    // Give the result to the user
    if (ok)
    {
        LED_PASS_PORT.OUTCLR = LED_PASS_PIN_MASK;
    }
    else
    {
        LED_FAIL_PORT.OUTCLR = LED_FAIL_PIN_MASK;
    }

    // Spit the results out the serial port
    UART_Setup();
    UART_SendString(applicationSerialBuffer, strlen(applicationSerialBuffer));
    UART_SendString(bootSerialBuffer, strlen(bootSerialBuffer));

    while(1) {}
}

/*! Define that selects the Usart used in example. */
#define USART USARTC0

// Spit the results out the serial port
void UART_Setup()
{
    // PortC Tx as output
    PORTC.DIRSET = PIN3_bm;

	// USARTC0, 8 Data bits, No Parity, 1 Stop bit
	USART_Format_Set(&USART, USART_CHSIZE_8BIT_gc, USART_PMODE_DISABLED_gc, false);

    // 9600bps @ 2MHz
    USART_Baudrate_Set(&USART, 12 , 0);

    // Enable the transmitter
    USART_Tx_Enable(&USART);
}

void UART_SendString(char* buffer, int length)
{
    int i;
    for (i = 0 ; i < length ; ++i)
    {
        while(!USART_IsTXDataRegisterEmpty(&USART))
        {
        }

		USART_PutChar(&USART, buffer[i]);
    }
}

And, the SP routine to do it (gcc)


;--------------------------------------------------------------------------------------------------
; This routine calculates a CRC for a given range of flash
;
; C Prototype:
;     uint32_t SP_FlashRangeCRC(uint32t startAddress, uint32_t endAddress);
;
; Input:
;     R21:R20:R19:R18 - Ending address for CRC, in bytes
;     R25:R24:R23:R22 - Starting address for CRC, in bytes. CRC Includes this address
;
; Returns:
;     R25:R24:R23:R22 - 32-bit CRC result (actually only 24-bits used)
;
; Side Effects:
;
;--------------------------------------------------------------------------------------------------
;
; 30.11.2.6 Flash Range CRC
;
; The Flash Range CRC command can be used to verify the content in an address range in Flash
; after a self-programming.
;
; 1. Load the NVM CMD register with the Flash Range CRC command.
; 2. Load the start byte address in the NVM Address Register (NVM ADDR).
; 3. Load the end byte address in NVM Data Register (NVM DATA).
; 4. Set the CMDEX bit in the NVM CTRLA register. This requires the timed CCP sequence
;    during self-programming.
;
; The BUSY flag in the NVM STATUS register will be set, and the CPU is halted during the execution
; of the command.

; The CRC checksum will be available in the NVM DATA register.
; In order to use the Flash Range CRC all the Boot Lock Bits must be unprogrammed (no locks).
; The command execution will be aborted if the Boot Lock Bits for an accessed location are set.
;--------------------------------------------------------------------------------------------------
.section .text
.global SP_FlashRangeCRC

SP_FlashRangeCRC:

    ; Load the NVM CMD Register with the flash range CRC command
    ldi     r25, NVM_CMD_FLASH_RANGE_CRC_gc
    sts     NVM_CMD, r25 

    ; Load the starting address in the NVM Address register
    sts     NVM_ADDR0, r22
    sts     NVM_ADDR1, r23
    sts     NVM_ADDR2, r24

    ; Load the end address in the NVM data register
    sts     NVM_DATA0, r18
    sts     NVM_DATA1, r19
    sts     NVM_DATA2, r20

    ; Command the sequence
    ldi   r18, CCP_IOREG_gc   ; Prepare Protect IO-register signature in R18.
    ldi   r19, NVM_CMDEX_bm   ; Prepare bitmask for setting NVM Command Execute bit into R19.
    sts   CCP, r18            ; Enable IO-register operation (this disables interrupts for 4 cycles).
    sts   NVM_CTRLA, r19      ; Load bitmask into NVM Control Register A, which executes the command.
    lds   r22, NVM_DATA0      ; Load NVM Data Register 0 into R22.
    lds   r23, NVM_DATA1      ; Load NVM Data Register 1 into R23.
    lds   r24, NVM_DATA2      ; Load NVM Data Register 2 into R24.
    clr   r25                 ; Clear R25 in order to return a clean 32-bit value.
    ret 

Outputs the following over serial:

Application: Range = 0x00FCD714, Calc = 0x00FCD714
Boot:        Range = 0x00BDA0A3, Calc = 0x00BDA0A3

-- Damien.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

...on a ATxmega32A4...

OK, 'fess up--ordered from distis? Samples?

I put in for samples the day there was a link and no word since. (But I think they cut back the rep in my area that used to do the follow-up.)

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

OK, 'fess up--ordered from distis? Samples?

Samples from disti. But our HQ guy has been asking for them since December, probably required all sorts of dirty tricks to get them.

That, and the TQFP44 adaptor board didn't come with the correct sandwich card, so that needed to get ordered in.

4 weeks later. Grrr....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
/******************************************************************************
         *
         * Function Name:  XmegaCrcCalculation
         *
         * Description: This calculates an XMEGA CRC over the specified range.
         *
         *****************************************************************************/

        static int XmegaCrcCalculation( int startAddr, int endAddr )
        {
            // Polynomial for use with Xmega 'A' devices
            const int CRC32_POLY = 0x0080001B;
	        int wordAddr;
	        int dataWord, tmpVar1, tmpVar2;
	        int crc = 0;
        	
	        // Calculate the CRC over the specified address range
	        for( wordAddr = startAddr; wordAddr < endAddr; wordAddr += 2 )
	        {
		        tmpVar1  = crc << 1;
		        tmpVar1 &= 0x00FFFFFE; // Always act as 24-bit variable
		        tmpVar2  = crc & (1 << 23);
        		
		        if ( tmpVar2 != 0 )
		        {
			        tmpVar2 = 0x00FFFFFF;
		        }
        		
		        // Read a single (2-byte) word from memory
		        dataWord = (int) flashImage[wordAddr]
                    | ((int) flashImage[wordAddr + 1] << 8);
        		
		        crc = ( tmpVar1 ^ dataWord ) ^ ( tmpVar2 & CRC32_POLY );
		        crc = crc & 0x00FFFFFF;
	        }
        	
	        // Return the calculated CRC
	        return crc;
        }