How To Determine Stack Utilization

It is necessary to know the stack utilization levels when software is developed for systems requiring high reliability. A simple technique to determine the stack utilization of a program is watermarking. In this technique the stack is filled with a pattern, and the program is executed. When the stack is used the pattern is overwritten. When the program terminates the point from which the pattern is undisturbed tells us the stack utilization level.

Single Byte Pattern 8051 Code

The following code snipped uses a 1 byte pattern to determine stack utilization in 8051 microcontroller.

#include <stdint.h>

__sfr __at (0x81) SP;

/**
 * Fill the stack with pattern. Called at the beginning of the program.
 */
void stack_start(void)
{
        uint8_t __idata *p = (uint8_t __idata *) SP;

        /* The stack pointer points to a valid byte, don't overwrite it. */
        p++;

        while (1) {
                if ((uint8_t) p >= 128)
                        break;

                *p = 0x55; p++;
        }

        return;
}

/**
 * Search for non-pattern byte starting from max. memory. Returns the last
 * used stack location.
 */
uint8_t stack_end(void)
{
        uint8_t __idata *p = (uint8_t __idata *) 127;

        while (1) {
                if (*p != 0x55)
                        break;
                p--;
        }

        return (uint8_t) p;
}

void stop(void)
{
        while (1);
}

uint16_t factorial(uint16_t num)
{
        if (num == 0)
                return 1;
        else if (num == 1)
                return 1;
        else
                return num * factorial(num-1);
}

uint8_t stack_max;

int main()
{
        stack_start();
        factorial(5);
        stack_max = stack_end();

        stop();
        return 0;
}

Multi-Byte Pattern 8051 Code

It is possible that the user had stored exactly the same pattern at the end of the stack. This possibility can be greatly reduced by using a multi-byte pattern. The code for using a multi-byte pattern is shown below.

#include <stdint.h>

__sfr __at (0x81) SP;

#define    PATT_WIDTH 4
#define RAM_MAX 0x7F

uint8_t pattern[PATT_WIDTH] = { 0xDE, 0xAD, 0xBE, 0xEF };

/**
 * Fill the stack with pattern. Called at the beginning of the program.
 */
void stack_start(void)
{
        char i;
        uint8_t __idata *p = (uint8_t __idata *) SP;

        /* Align pointer to pattern width. */
        p += (4 - (SP % PATT_WIDTH));

        /* Filling the memory with the pattern */
        while (1) {
                if ((uint8_t) (p + PATT_WIDTH - 1) > RAM_MAX)
                        break;

                for (i = 0; i < PATT_WIDTH; i++) {
                        *p = pattern[i];
                        p++;
                }
        }

        return;
}

/**
 * Search for non-pattern byte starting from max. memory. Returns the last
 * used stack location.
 */
uint8_t stack_end(void)
{
        int i;
        uint8_t __idata *p = (uint8_t __idata *) (RAM_MAX);

        while (1) {
                for (i = 0; i < PATT_WIDTH; i++) {
                        if (*p != pattern[PATT_WIDTH - i - 1])
                                goto exit;
                        p--;
                }
        }

 exit:
        return (uint8_t) p;
}

void stop(void)
{
        while (1);
}

uint16_t factorial(uint16_t num)
{
        if (num == 0)
                return 1;
        else if (num == 1)
                return 1;
        else
                return num * factorial(num-1);
}

uint8_t stack_max;

int main()
{
        stack_start();
        factorial(5);
        stack_max = stack_end();

        stop();
        return 0;
}