2.7.2 Suggested Application Architecture

It is recommended that application, which make use of the SAM C21 Diagnostic Library, be architected as a state-machine. During diagnostics it will sometimes be necessary to do software resets during the POST diagnostics; ensure a clean state of registers for the SFR Reset calls; and a prestine state for peripheral modules that have their own mini-state machines internally, such as RSTC and WDT. The state-machine architecture is capable of supported those needs.

To maintain the reset state across resets, the reset state should be kept in a persistent variable as follows:

RESET_STATES __attribute__((section(".persist"))) reset_state;

Just before each software reset occurs (by calling NVIC_SystemReset()), the state variable is advanced to the next state, so after the reset occurs execution will continue there and jump past code already executed.

Note: that for the SAMC21J18A/E18A processors, a software reset takes about 10 ms. Resets should be minimized as much as possible to decrease the overall boot time.

Within each state, a lower level function is called to actually run the tests, e.g. DIAG_DMAC_SFRPost() and DIAG_DMAC_Post() for the DMAC peripheral (shown below). The result of each test is stored in a persistent array called diag_test_status.

The diagnostics are called from main() by calling DIAG_RunPOST(). This must be called before the SYS_Initialize() call. Because of the software resets, DIAG_RunPOST() will be called repeatily but will never return until the POST diagnostics are complete (state RESET_STATE_RUN). At that point, SYS_Initialize() is called to perform the normal application initialization, and if desired, the results of the tests that were saved off in diag_test_status can be displayed to a terminal via printf’s.

We recommend establishing the following files: main.c , diag.h, and diag.c. A post file is also recommended for each peripherial to call specific functions within the peripheral diagnostic API. Post_DMAC.c is included as an example.

Table 2-4. 
FileCode Example
main.c
extern DIAG_TEST_STATUS
        __attribute__((section(".persist"))) diag_test_status[NUM_DIAG_TESTS];

extern DIAG_TEST_STATUS
        __attribute__((section(".persist"))) core_status; 

int main ( void )
{
    DIAG_RunPOST();
    SYS_Initialize ( NULL ); // access to sercom for reporting

    printf("\f\r\nPOST results:\r\n\r\n"); 
    printf("\tCore \t\t= %d\r\n",core_status); 
    printf("\tDIVAS \t\t= %d\r\n",
    diag_test_status[DIAG_DIVAS_RESULT]); 
    printf("\tDMAC \t\t= %d\r\n",diag_test_status[DIAG_DMAC_RESULT]); 
    printf("\tPM \t\t= %d\r\n",diag_test_status[DIAG_PM_RESULT]); 

    /*
     * remainder of your application
     */
}
diag.h
typedef enum 
{
    /* Application's reset states */
    RESET_STATE_RUN = 0,

    RESET_STATE_DMAC = 0xd0db17e0; 
    RESET_STATE_MID_POST = 0x166cf399,  

    /*
     * additional enums
     */
} RESET_STATES;

enum diag_test
{
    DIAG_DIVAS_RESULT;
    DIAG_DMAC_RESULT; 
    DIAG_PM_RESULT; 
    /*
     * additional enums
     */
    NUM_DIAG_TESTS;  
}

diag.c
#include "diag_post.h"
#include "definitions.h"
#include "diagnostic/core/diag_core.h"

DIAG_TEST_STATUS
        __attribute__((section(".persist")))diag_test_status[NUM_DIAG_TESTS];
DIAG_TEST_STATUS
        __attribute__((section(".persist"))) core_status;
RESET_STATES
        __attribute__((section(".persist"))) reset_state;

void DIAG_RunPOST(void)
{
        DIAG_SetPOSTComplete(false);   
       
        NVMCTRL_REGS->NVMCTRL_CTRLB = NVMCTRL_CTRLB_RWS(3);
        NVIC_Initialize(); 
        DIAG_OSCCTRL_Initialize(48000000, 5.0); // Initialize Main Oscillator

        switch (reset_state)
        {   
            /* first state after power on reset is default */
            default:
                 /* intialize status  */ 
                 int i;
                 for (i = 0; i < NUM_DIAG_TESTS; i++)
                 {
                     diag_test_status[i] = DIAG_TEST_NOT_EXECUTED;
                 }
                 /* run Core diagnostics first */
                 DIAG_CORE_M0p_STL_Init(DIAG_CONTEXT_OOR);
                 core_status = DIAG_CORE_M0p_STL(DIAG_CONTEXT_OOR); 
                 reset_state = RESET_STATE_DMAC; 
                 /* do a software reset */
                 NVIC_SystemReset();
             } 

             /* run DMAC */ 
             case RESET_STATE_DMAC: 
             { 
                 diag_test_status[DIAG_DMAC_RESULT] = DIAG_DMAC_SFRPost();
                 if (DIAG_TEST_PASSED == diag_test_status[DIAG_DMAC_RESULT])
                 {
                     diag_test_status[DIAG_DMAC_RESULT] = DIAG_DMAC_Post();
                 }  
                 diag_test_status[DIAG_DIVAS_RESULT] = DIAG_DIVAS_Post();  
                 /*
                  * etc. etc. – many more states not shown
                  */          
                 reset_state = RESET_STATE_MID_POST;
                 NVIC_SystemReset();  
              } 

              /* run MID post tests */
              case RESET_STATE_MID_POST: 
              {
                  diag_test_status[DIAG_PM_RESULT] = DIAG_PM_SFRPost(); 
                  /*
                   * etc. etc. – many more states not shown
                   */
                  reset_state = RESET_STATE_HARMONY_POST; 
                  /* perform a software reset */
                  NVIC_SystemReset(); 
              } 

              /* startup harmony application */
              case RESET_STATE_HARMONY_POST: 
              {
                  DIAG_SetPOSTComplete(true);
                  /* in case reset occurs, will start over with tests */
                  reset_state = RESET_STATE_RUN;  
              } 
              break; 
          }  

}

Post_DMAC.c
DIAG_TEST_STATUS DIAG_DMAC_SFRPost()
{   
    DIAG_TEST_STATUS result;
    
    result = DIAG_DMAC_SFRReset(NULL,0,false);
    
    if (result == DIAG_TEST_PASSED)
    {
        result = DIAG_DMAC_SFRWriteRead(NULL,0,false);
    }
       
    return result; 
}

DIAG_TEST_STATUS DIAG_DMAC_Post()
{   
    DIAG_TEST_STATUS result;
       
    result = DIAG_DMAC_Transfer(DMAC_CHANNEL_9);

    if (result == DIAG_TEST_PASSED)
    {    
        result = DIAG_DMAC_Interrupts(DMAC_CHANNEL_9); 

        if (result == DIAG_TEST_PASSED)
        {    
            result = DIAG_DMAC_LinkedList(DMAC_CHANNEL_9);
        }
    }
    
    return result; 
}