This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1110 - forcing a reset from software

Other Parts Discussed in Thread: CC2510, CC2530

Dear All,

Is there a way to force a reset from software on CC1110 without using the watchdog?

An illegal operation command to force a reset for example?

Our firmware can be upgraded remotely over the air. After firmware upgrade (replacing the new firmware with the old version) a reset is necessary. We could get the reset from the watchdog but what if watchdog is not enabled?

 

Zoltan

  • Zoltan,

    If the watchdog is not enabled, then disable interrupts, enable the watchdog, set to minimum time period, start watchdog, and wait for the reset.  It won't matter if something else in the system was using it since the system is going to be reset anyways.

    If the watchdog is in use, disable interrupts, set the time period to minimum, reset the watchdog, and wait for the reset.

    If you don't need to do a hard reset, i.e. you don't need to have all the registers reset to their default states, you can do a soft reset by jumping directly to the reset vector which will restart the code.  You have to make sure you initialize all registers if you choose this form.  Also, if the watchdog is running, it is possible for it to issue a hard reset before your initialization is complete.  If you want to do a soft reset use the following statement

    (*((void (* __code *)(void))0))();

    The above is for IAR compilers.  You may need a slightly different style of modifier (__code) on another compiler.

    A little explaining if you like.  The reset vector is at address zero in code space so we can use the value of zero to point to the reset vector which points to the entry point of the program.  Thus zero (0) is a pointer to a pointer in code space to a function.  Technically, the entry point of code is not usually considered a function but for our purposes, we can consider it to act like a function that takes no parameters and returns no parameters.  So if we cast the value of zero to be a pointer to a pointer in code space to a function and then dereference it so we can call it then the compiler will generate code to jump to the entry point of our code.

    It works like this, this is a simple pointer to a function that takes no parameters and returns nothing.  The parenthesis around the * are required so the compiler will bind the pointer operator to the postfixed function call operator pair ( ).

    void (*)(void)

    We can make it a pointer to a pointer to a function by simply adding another layer of dereferencing, i.e. another * like this

    void (**)(void)

    But since we know the last pointer to the function is the reset vector and it resides in code space, we need to use an implementation specific modifier to tell the compiler to load the value from code space giving

    void (* __code *)(void)

    We could have used __xdata as well since the CC1110 overlays xdata and code space at address zero to the same physical RAM location.  Also, if using a memory model which by default puts variables in xdata, then we could have skipped this step entirely.  However, use of the __code modifier is more explicit as to the purpose of the type.

    Now that we have the type defined, use it to cast the value of zero like this

    (void (* __code *)(void))0

    At this point we have a pointer to a pointer in code space to a function that takes no arguments and returns none.  Since we want to call the function, we must dereference the first reference of this type as

    *((void (* __code *)(void))0)

    Now we have a pointer in code space to a function which takes no arguments and returns none.  All we need to do now is call it.  Use a couple of more parenthesis to manage the binding precedence and you get

    (*((void (* __code *)(void))0))( );

    Ta da!

    Regards,

    Jim

     

  • Dear Jim,

    Thank you for the clear and detailed explanation!

    I have tried the solution: (*((void (* __code *)(void))0))( );

    It was not working and I started investigating the reason. I realized this is not true in my program:  "Thus zero (0) is a pointer to a pointer in code space to a function. "
    On 0x0000 there is LJMP __program_start. This jump instruction will sends the program to __program_start. The reset vector is actually on 0x0001 - 0x0002.

    So the above code has to be modified to (*((void (* __code *)(void))1))( );

     

    Regards,

    Zoltan

  • Zoltan,

    You are correct.  I was thinking that only the pointer to the program start was located here but in actuality, a reset of the 8051 processor sets the program counter to address zero and starts execution there.  Thus the LJMP instruction at address 0 to the entry point.  So this can simplify things slightly as the value zero can really be considered a pointer to a function rather than a pointer to a pointer to a function.  This makes the line of code look like this

    ((void (*)(void))0)();

    Now it will jump to the LJMP instruction at address zero via a single indirection.  Note that a pointer to a function, by definition, is in the code space so the modifier is not necessary at this level.

    Your investigation shows that the reset vector is effectively part of the LJMP instruction and your modification of my code is also valid.  However, calling the LJMP instruction directly will generate smaller tighter code and actually execute faster.

    Compiling with your corrected code generates the following assembly

    Calling the LJMP instruction directly yeilds

    Which, even though we are adding the additional LJMP instruction in the call, overall the code is tighter and faster to call the LJMP instruction at address zero.

    In any case, very good work on correcting my mistake!

    Jim

     

  • Hi Zoltan,

    I am trying to do firmware upgrade remotely over the air for CC1110.  Can you share with me your mechanism or point me to some technical source so that I can do that myself?  Any guideline will be appreciated.

    Thanks in advance,

    Chau

  •  

    Dear Zoltan,

    I am working on CC1110 firmware update too and having the erase problem (i.e. erase the old code before writing the new code). Most of the time, the process is going south after the erase complete and the write never starts. Did you experience the same problem? If so, how did you solve this problem? BTW, what compiler are you using? Is that IAR?

     

    Joe.

  • Hello Joe,

    I recently used the boot loader for CC2530 and modified it to work on CC2510. In this boot loader, firmware which needs to be written into the flash comes from external controller through SPI/UART interface. Firmware comes in the chunk of 64 bytes. Before writing this data, flash must be erased. So I have to erase and then write this firmware in a chunk of 64 bytes. The smallest unit which can be erased in a flash is "Page" which is 1024 bytes in case of CC2510 (I guess the page size is same for CC1110). So in my case, I erase the page (1024 bytes) once when I am writing at starting location of the page (i.e. first 64 bytes of a page). I do not issue erase command while writing the remaining 1024 - 64 = 960 bytes. This puts one limitation that I cannot start writing from the middle of the page because in that case, erase command for that page will not be issued.

    For erasing a page, you need to specify the page number in FADDRH[5:1] and then set FCTL.ERASE

    Datasheet also states that after writing into FCTL.ERASE, next instruction should be a NOP instruction. After flash erase, execution will continue from the instruction after NOP instruction.

    Regards,

    Shashank