Yahoo Groups archive

AVR-Chat

Index last updated: 2026-04-28 22:41 UTC

Thread

Fuse detection

Fuse detection

2004-06-10 by Bruce Parham

All, 

I just got nailed by a miss programmed (by me of course) M128 fuse. After spending a few 
hours tracking down the problem, it occured to me that my application probably should
check for this kind of situation at startup and do something if things aren't right. 

The code below is the first cut; it just reads and displays the current settings. It will 
fail if the M128C fuse is programmed (haven't figured out the best way to detect that yet) 
but otherwise works fine. 

The code was written for the ICCAVR compiler but should be adaptable to others.

Bruce

---------------------------------------------------------------------------

struct M128_FUSES
{
    unsigned cksel      : 4;    // low byte
    unsigned sut        : 2;
    unsigned boden      : 1;
    unsigned bodlevel   : 1;
    
    unsigned bootrst    : 1;    // high byte
    unsigned bootsz     : 2;
    unsigned eesave     : 1;
    unsigned ckopt      : 1;
    unsigned spien      : 1;
    unsigned jtagen     : 1;
    unsigned ocden      : 1;
    
    unsigned wdton      : 1;    // extension byte
    unsigned m103c      : 1;
    unsigned unused     : 6;
    
    unsigned lb         : 2;    // lock bit byte
    unsigned blb0       : 2;
    unsigned blb1       : 2;
};

union   FUSES
{
    struct M128_FUSES f_bits;
    unsigned long fuse_state; 
} fuses;

unsigned long get_fuse(void)    // get fuses to test clock source and stuff...
{
    asm
    (
        "ldi    r26, 9    ; BLBSET & SPMEN         \n"
        "mov    r0, r26   ; to read fuses          \n"
        "ldi    r26, 0x68 ; spmcsr                 \n"
        "ldi    r27, 0                             \n"
        "ldi    r30, 0    ; start with FLB         \n"
        "ldi    r31, 0                             \n"
        "st     x, r0     ; prime the pump         \n"
        "lpm    r16, z+   ; fuse low byte -> R16   \n"
        "st     x, r0                              \n"
        "lpm    r19, z+   ; lock byte -> R19       \n"
        "st     x, r0                              \n"
        "lpm    r18, z+   ; fuse ext byte -> R18   \n"
        "st     x, r0                              \n"
        "lpm    r17, z    ; fuse high byte -> R17  \n"
        "ret                                       \n"
    );
    return(0); // keep the compiler from whinning
}

void show_fuses(void)   // display M128 fuses @ startup
{
    fuses.fuse_state = get_fuse();
    
    printf("\nProcessor Fuses:\n\n");
    
    printf("  cksel = %1u      sut = %1u    boden = %1u   bodlevel = %1u\n", 
    fuses.f_bits.cksel, fuses.f_bits.sut, fuses.f_bits.boden, fuses.f_bits.bodlevel);
    
    printf("bootrst = %1u   bootsz = %1u   eesave = %1u      ckopt = %1u\n", 
    fuses.f_bits.bootrst, fuses.f_bits.bootsz, fuses.f_bits.eesave, fuses.f_bits.ckopt);
    
    printf("  spien = %1u   jtagen = %1u    ocden = %1u      wdten = %1u\n", 
    fuses.f_bits.spien, fuses.f_bits.jtagen, fuses.f_bits.ocden, fuses.f_bits.wdton);
    
    printf("  M103c = %1u       lb = %1u     blb0 = %1u       blb1 = %1u\n", 
    fuses.f_bits.m103c, fuses.f_bits.lb, fuses.f_bits.blb0, fuses.f_bits.blb1);
}

Re: Fuse detection

2004-06-10 by Bruce Parham

It turns out that M103 mode detection on the M128 is fairly straight forward. If the 
M103 fuse is programmed, the SPMCSR register is disabled and replaced by RAM. The
trick is to set the SPMEN bit and wait for more than four clocks. If it's still set
after four clocks, it's RAM and the M103C fuse must be set. Otherwise, the SPMCSR
is active and the M103C fuse is unprogrammed. 

The stuff below reflects this addition.

Bruce

----------------------------------------------------------------------------------

struct M128_FUSES
{
    unsigned cksel      : 4;    // low byte
    unsigned sut        : 2;
    unsigned boden      : 1;
    unsigned bodlevel   : 1;
    
    unsigned bootrst    : 1;    // high byte
    unsigned bootsz     : 2;
    unsigned eesave     : 1;
    unsigned ckopt      : 1;
    unsigned spien      : 1;
    unsigned jtagen     : 1;
    unsigned ocden      : 1;
    
    unsigned wdton      : 1;    // extension byte
    unsigned m103c      : 1;
    unsigned unused     : 6;
    
    unsigned lb         : 2;    // lock bit byte
    unsigned blb0       : 2;
    unsigned blb1       : 2;
};

union   FUSES
{
    struct M128_FUSES f_bits;
    unsigned long fuse_state; 
} fuses;

unsigned long get_fuse(void)    // get fuses to test clock source and stuff...
{
    asm
    (
        "ldi    r26, 9    ; BLBSET & SPMEN          \n"
        "mov    r0, r26   ; to read fuses           \n"
        "ldi    r26, 0x68 ; spmcsr                  \n"
        "ldi    r27, 0                              \n"
        "ldi    r30, 0    ; start with FLB          \n"
        "ldi    r31, 0                              \n"
        "ldi    r18, 0    ; in case M103C is set    \n"
        "ld     r1, x     ; in case it's just ram   \n"
        "st     x, r0     ; set the spmen bit       \n"
    );

    asm
    (
        "nop              ; timeout the spmen bit   \n"
        "nop                                        \n"
        "nop                                        \n"
        "nop                                        \n"
        "nop                                        \n"
        "nop                                        \n"
        "ld     r16, x    ; spmcsr or ram           \n"
        "sbrs   r16, 0    ; skip if spmen still set \n"
        "rjmp   get_fuse1 ; M103C fuse not set!     \n"
        "st     x, r1     ; replace ram value...    \n"
        "rjmp   get_fusex ; bomb out w/R18 zero     \n"
    );

    asm
    (
        "get_fuse1:                                 \n"
        "st     x, r0     ; prime the pump          \n"
        "lpm    r16, z+   ; fuse low byte -> R16    \n"
        "st     x, r0                               \n"
        "lpm    r19, z+   ; lock byte -> R19        \n"
        "st     x, r0                               \n"
        "lpm    r18, z+   ; fuse ext byte -> R18    \n"
        "st     x, r0                               \n"
        "lpm    r17, z    ; fuse high byte -> R17   \n"
    );

    asm
    (
        "get_fusex:                                 \n"
        "adiw   r28, 4    ; r20-r23 was pushed      \n"
        "ret    \n"
    );

    return(0); // keep the compiler from whinning
}


// Display M128 fuses @ startup. 
// Returns TRUE if in M128 mode, FALSE if M103 mode

int show_fuses(void)   
{
    fuses.fuse_state = get_fuse();
    
    if(fuses.f_bits.m103c == 0)     // ack! 103 emulation...
    {
        printf("\033[2J\nProcessor is in M103 Mode, Fuse access denied!");
        return(0); 
    }
    
    printf("\nProcessor Fuses:\n\n");
    
    printf("  cksel = %1u      sut = %1u    boden = %1u   bodlevel = %1u\n", 
    fuses.f_bits.cksel, fuses.f_bits.sut, fuses.f_bits.boden, fuses.f_bits.bodlevel);
    
    printf("bootrst = %1u   bootsz = %1u   eesave = %1u      ckopt = %1u\n", 
    fuses.f_bits.bootrst, fuses.f_bits.bootsz, fuses.f_bits.eesave, fuses.f_bits.ckopt);
    
    printf("  spien = %1u   jtagen = %1u    ocden = %1u      wdten = %1u\n", 
    fuses.f_bits.spien, fuses.f_bits.jtagen, fuses.f_bits.ocden, fuses.f_bits.wdton);
    
    printf("  M103c = %1u       lb = %1u     blb0 = %1u       blb1 = %1u\n", 
    fuses.f_bits.m103c, fuses.f_bits.lb, fuses.f_bits.blb0, fuses.f_bits.blb1);

    return(-1);
}

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.