Assertion Handler
The assertion handler is simply a C function that never returns and will eventually trigger a system reset. The prototype for the Windows port is:
__declspec(noreturn) void CWPortAssert(int8_t *file, uint16_t line);
In Visual Studio C a warning that the function does not return is suppressed by the __declspec(noreturn) attribute. The exact manner to suppress this warning will vary between toolchains.
The assertion handler takes two parameters a file string, and a line number. These are passed into CWPortAssert() using the __FILE__ and __LINE__ macros. This allows implementors to pinpoint the exact source file and line on which the assertion occurred.
Besides never returning, the most critical and sometimes difficult to implement feature of the assertion handler is to report the file and line number of the error. Some systems may output the error information in more than one way, and which could vary depending on if the build is for debug or production systems.
One consideration when planning for reporting the error is that the mere existence of a detected error may indicate that the system is unstable (heap corrupted, stack overflowed, etc.) Therefore, we cannot always expect system functions to behave properly beyond this point. For example, calling a printf() function that is mapped to output to a serial interface, or logging a message to non-volatile memory may not work reliably.
The most general solution to this Catch-22 problem is to partition a region of SRAM, perhaps as few as 32 bytes, that will not be used by the linker for any purpose, details of how to do this will vary by toolchain.
The assertion handler would write the file and line information to this region of SRAM and then invoke a system reset. Because SRAM typically persists through a system reset the application startup code can detect if the last reset was caused by an assertion. And at that point either output the error to the serial port or log it to non-volatile memory. The application code would use a MAGIC number to signal if the SRAM has valid contents or not.
Ideally the application code, after initializing the CWL, would also generate an OKM that contains the assertion error. This would greatly simplify remote diagnostics.
Short of implementing the general solution described above minimizing system interactions while outputting the file and line information may be possible in other ways. For example, the assertion handler may turn off interrupts and write the file and line information byte by byte directly to the UART TX hardware register as it empties.
Regardless of the exact means, outputting the file and line number in a reliable manner is essential for diagnostic purposes.
MSP430 Pseudocode Example
This MSP430 platform assertion handler logs the assertion file and line number to SRAM, turns on the platform LEDs, and finally resets the system.
Upon entering the assertion handler, global interrupts are disabled so that the assertion handler is not interrupted.
Next, pointers to the magic number, line number, and file name are setup. The magic number when set indicates that the assertion file and line number are valid. The function assigns the line number and copies as much of the file name, which also includes path information, to the persistent SRAM.
The magic number, file and line number are logged to a region of SRAM that is not mapped by the linker for any other purpose. This means that the SRAM will persist through a reset and not be touched by the C environment startup code. Some platforms may have special keywords to define variables that are meant to persist through a reset.
The assertion handler then forces a watchdog reset.
When the system boots, the code will check for a valid magic number. If valid then the file and line number of the assertion can be logged and/or put into an application layer heartbeat that is sent to the OKC.
void CWPortAssert(int8_t *fileStr, uint16_t line)
{
CS_ENTER();
/* Store assertion to memory, will be logged on next boot */
uint32_t *magic = (uint32_t *) ASSERTION_ADDR_MAGIC_ADDR ;
uint16_t *linep = (uint16_t *) ASSERTION_ADDR_LINE_ADDR ;
uint8_t *filep = (uint8_t *) ASSERTION_ADDR_FILE_ADDR ;
uint16_t len ;
*linep = line; /* Store the line number */
len = strlen(fileStr); /* Store the last part of the file name path */
if (len <= ASSERION_MAX_FILE_LEN)
{
/* Can fit the whole file name and path */
strncpy((char *) filep, fileStr, ASSERION_MAX_FILE_LEN);
}
else
{
/* Need to just grab the end */
strncpy((char *) filep, &fileStr[len – ASSERION_MAX_FILE_LEN], ASSERION_MAX_FILE_LEN);
}
filep[ASSERION_MAX_FILE_LEN] = 0; /* Ensure NULL termination */
*magic = ASSERTION_ADDR_MAGIC_VAL; /* Magic num == assert data valid */
WDTCTL = WDTHOLD; /* Will immediately generate watchdog reset */
for (;;); /* Should not get here */
}