LPC Signature Generator

30 Sep 2013

Has anybody ever used the signature generator on the NXP microcontrollers? I noticed it at 20.16.4.7 on page 403 of the LPC11UXX user manual while I was reading up on the IAP commands. I could see this being extremely useful for control devices that store data in the flash. If the data is ever corrupted, the system could detect it and refuse to run as opposed to just going haywire. The way I see it there are a couple of things that have to happen to make this work... First of all, the calling code needs to run from RAM, so does anybody know how to define RAM-based functions using the online compiler? I'm not sure how important this is, since I was able to generate a signature WITHOUT running from RAM, but who knows maybe it could fail at some point? Secondly, there needs to be a desktop application that can generate reference signatures for a block of data. There's pseudo code in the user manual, but I couldn't get it to work in C#... Anyway, here's the code I used to generate a signature for the entire flash contents on an LPC11U24:

#include "mbed.h"

DigitalOut myled(LED1);

int main()
{
    unsigned int sig[4];

    //Print the generating message
    printf("\nStarting signature generation...\n\n");

    //Make sure the done flag is cleared
    printf("Clearing done flag...");
    LPC_FLASHCTRL->FMSTATCLR = (1 << 2);
    printf("done!\n");

    //Set the 128-bit start and stop word addresses
    printf("Setting start and stop addresses...");
    LPC_FLASHCTRL->FMSSTART = (0x00000000 >> 4) & 0x0001FFFF;
    LPC_FLASHCTRL->FMSSTOP = (0x00007FFF >> 4) & 0x0001FFFF;
    printf("done!\n");

    //Start the signature generation
    printf("Starting generator...");
    LPC_FLASHCTRL->FMSSTOP |= (1 << 17);
    printf("done!\n");

    //Wait for the signature generation to complete
    printf("Waiting for done flag...");
    while(!(LPC_FLASHCTRL->FMSTAT & (1 << 2)));
    printf("done!\n");

    //Clear the done flag
    printf("Clearing done flag...");
    LPC_FLASHCTRL->FMSTATCLR = (1 << 2);
    printf("done!\n");

    //Copy the signature to RAM
    printf("Copying signature...");
    sig[0] = LPC_FLASHCTRL->FMSW0;
    sig[1] = LPC_FLASHCTRL->FMSW1;
    sig[2] = LPC_FLASHCTRL->FMSW2;
    sig[3] = LPC_FLASHCTRL->FMSW3;
    printf("done!\n");

    //Print the signature
    printf("\nSignature: %u-%u-%u-%u", sig[0], sig[1], sig[2], sig[3]);

    while(1) {
        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}
01 Oct 2013

And we have progress!!! It turns out that AN11208 over at LPCware has the code for generating a signature in software! In light of this, I think the obvious course of action is to create an mbed library that will work on any mbed-enabled microcontroller. The hardware-based code will be used on LPC microcontrollers, while the software-based code will be used on everything else. Here's the software routine from the app note:

/***************************************************************************//**
** @brief 		Function starts the execution of the software signature generation.
** @param[in]	startAddrs		This variable is the starting address of where the signature generation will start.
** @param[in]	length			The length variable is the region size to used for the signature generation.
** @param[in]	*pResultSign	The result after generation completion will be put in the pointed location by the pResultSign pointer.
** @return		NONE
*****************************************************************************/
void StartSoftSignatureGen (UINT32 startAddr, UINT32 length, FlashSign_t *pResultSign)
{
	FlashSign_t flashWord;
	FlashSign_t refSignature = {0,0,0,0};
	FlashSign_t nextSign;

	UINT32 		*PageAddr = 0,
				i;

	PageAddr = (UINT32 *)((UINT32)startAddr);

	for ( i = 0; i <= (length>>4); i++ )
	{
		flashWord.word0 = *PageAddr;
		PageAddr++;
		flashWord.word1 = *PageAddr;
		PageAddr++;
		flashWord.word2 = *PageAddr;
		PageAddr++;
		flashWord.word3 = *PageAddr;
		PageAddr++;

	    /* update 128 bit signature */
	    nextSign.word0 = flashWord.word0 ^ refSignature.word0>>1 ^ refSignature.word1<<31;
	    nextSign.word1 = flashWord.word1 ^ refSignature.word1>>1 ^ refSignature.word2<<31;
	    nextSign.word2 = flashWord.word2 ^ refSignature.word2>>1 ^ refSignature.word3<<31;
	    nextSign.word3 = flashWord.word3 ^ refSignature.word3>>1 ^
	                     ( refSignature.word0 & 1<<29 )<<2 ^
	                     ( refSignature.word0 & 1<<27 )<<4 ^
	                     ( refSignature.word0 & 1<<2 )<<29 ^
	                     ( refSignature.word0 & 1<<0 )<<31;

	    /* point to the calculated value */
	    refSignature.word0 = nextSign.word0;
	    refSignature.word1 = nextSign.word1;
	    refSignature.word2 = nextSign.word2;
	    refSignature.word3 = nextSign.word3;
	}

	/* Copy the reference signature to the result pointer */
	pResultSign->word0 = refSignature.word0;
	pResultSign->word1 = refSignature.word1;
	pResultSign->word2 = refSignature.word2;
	pResultSign->word3 = refSignature.word3;

}


Now all we need is a way to run the hardware-based routine (or at least part of it) in RAM. It doesn't look like RVDS has any magic attributes for placing functions in RAM, you have to use Keil to do it. That being said, all this code is doing is poking a few registers, so it shouldn't be hard to cheat the system and do it at runtime. Anybody have any ideas?

01 Oct 2013

Well, after some initial confusion I was able to port the hardware and software routines from AN11208 and run them on an LPC11U24. There are a few math bugs in the AN11208 code that show up when you try to generate a signature for anything but the entire flash, so I had to make some modifications to get everything to work. Here's what I have so far:

#include "mbed.h"

typedef struct {
    unsigned int word0; //Word 0 of 128-bit signature (bits 31 to 0).
    unsigned int word1; //Word 1 of 128-bit signature (bits 63 to 32).
    unsigned int word2; //Word 2 of 128-bit signature (bits 95 to 64).
    unsigned int word3; //Word 3 of 128-bit signature (bits 127 to 96).
} FLASH_SIG_Type;

void hardSig(unsigned int startAddr, unsigned int length, FLASH_SIG_Type *pSig)
{
    //Make sure the done flag is cleared
    LPC_FLASHCTRL->FMSTATCLR = (1 << 2);

    //Convert the byte addresses to 128-bit flash word addresses
    startAddr = (startAddr >> 4) & 0x0001ffff;
    length = (startAddr + ((length - 1) >> 4)) & 0x0001ffff;

    //Write the start address
    LPC_FLASHCTRL->FMSSTART = startAddr;

    //Write stop address and start the signature generator
    LPC_FLASHCTRL->FMSSTOP = length | (1 << 17);

    //Wait for signature to be generated
    while(!(LPC_FLASHCTRL->FMSTAT & (1 << 2)));

    //Clear the done flag
    LPC_FLASHCTRL->FMSTATCLR = (1 << 2);

    //Store the signature words in the structure
    pSig->word0 = LPC_FLASHCTRL->FMSW0;
    pSig->word1 = LPC_FLASHCTRL->FMSW1;
    pSig->word2 = LPC_FLASHCTRL->FMSW2;
    pSig->word3 = LPC_FLASHCTRL->FMSW3;
}

void softSig(unsigned int startAddr, unsigned int length, FLASH_SIG_Type *pSig)
{
    FLASH_SIG_Type flashWord;
    FLASH_SIG_Type refSignature = {0, 0, 0, 0};
    FLASH_SIG_Type nextSign;
    unsigned int* PageAddr = (unsigned int*)((unsigned int)startAddr);

    for (unsigned int i = 0; i < (length >> 4); i++) {
        flashWord.word0 = *PageAddr;
        PageAddr++;
        flashWord.word1 = *PageAddr;
        PageAddr++;
        flashWord.word2 = *PageAddr;
        PageAddr++;
        flashWord.word3 = *PageAddr;
        PageAddr++;

        //Update 128 bit signature
        nextSign.word0 = flashWord.word0 ^ refSignature.word0 >> 1 ^ refSignature.word1 << 31;
        nextSign.word1 = flashWord.word1 ^ refSignature.word1 >> 1 ^ refSignature.word2 << 31;
        nextSign.word2 = flashWord.word2 ^ refSignature.word2 >> 1 ^ refSignature.word3 << 31;
        nextSign.word3 = flashWord.word3 ^ refSignature.word3 >> 1 ^
                         (refSignature.word0 & 1 << 29) << 2 ^
                         (refSignature.word0 & 1 << 27) << 4 ^
                         (refSignature.word0 & 1 << 2) << 29 ^
                         (refSignature.word0 & 1 << 0) << 31;

        //Point to the calculated value
        refSignature.word0 = nextSign.word0;
        refSignature.word1 = nextSign.word1;
        refSignature.word2 = nextSign.word2;
        refSignature.word3 = nextSign.word3;
    }

    //Copy the reference signature to the result pointer
    pSig->word0 = refSignature.word0;
    pSig->word1 = refSignature.word1;
    pSig->word2 = refSignature.word2;
    pSig->word3 = refSignature.word3;
}

//Flash word 1024 (0x400)
const char dummyData1[16] __attribute__((at(0x4000))) = {
    0xFF, 0x00, 0xFF, 0x11, 0xFF, 0x22, 0xFF, 0x33, 0xFF, 0x44, 0xFF, 0x55, 0xFF, 0x66, 0xFF, 0x77
};

//Flash word 1025 (0x401)
const char dummyData2[16] __attribute__((at(0x4010))) = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};

//Flash word 1026 (0x402)
const char dummyData3[16] __attribute__((at(0x4020))) = {
    0x11, 0x22, 0x44, 0x88, 0xFF, 0xFF, 0x88, 0x44, 0x22, 0x11, 0x11, 0x22, 0x44, 0x88, 0xFF, 0xFF
};

int main()
{
    FLASH_SIG_Type sig1;
    hardSig((unsigned int)dummyData1, 16, &sig1);
    printf("\nHard Signature 1: 0x%x%x%x%x", sig1.word3, sig1.word2, sig1.word1, sig1.word0);

    FLASH_SIG_Type sig2;
    softSig((unsigned int)dummyData1, 16, &sig2);
    printf("\nSoft Signature 1: 0x%x%x%x%x", sig2.word3, sig2.word2, sig2.word1, sig2.word0);

    FLASH_SIG_Type sig3;
    hardSig((unsigned int)dummyData2, 16, &sig3);
    printf("\nHard Signature 2: 0x%x%x%x%x", sig3.word3, sig3.word2, sig3.word1, sig3.word0);

    FLASH_SIG_Type sig4;
    softSig((unsigned int)dummyData2, 16, &sig4);
    printf("\nSoft Signature 2: 0x%x%x%x%x", sig4.word3, sig4.word2, sig4.word1, sig4.word0);

    FLASH_SIG_Type sig5;
    hardSig((unsigned int)dummyData3, 16, &sig5);
    printf("\nHard Signature 3: 0x%x%x%x%x", sig5.word3, sig5.word2, sig5.word1, sig5.word0);

    FLASH_SIG_Type sig6;
    softSig((unsigned int)dummyData3, 16, &sig6);
    printf("\nSoft Signature 3: 0x%x%x%x%x", sig6.word3, sig6.word2, sig6.word1, sig6.word0);
}


The code will generate signatures for 3 different flash words using both routines. Obviously the hardware routine is running out of flash instead of RAM, but it appears to work just fine...

02 Oct 2013

Just finished an initial port of the software generator routine to C#. This is basically a straight port and hasn't been optimized for C# at all, but I tested it out on 16, 32, and 48 byte arrays and it does work:

public struct FLASH_SIG_Type {
    public uint word0;    //Word 0 of 128-bit signature (bits 31 to 0).
    public uint word1;    //Word 1 of 128-bit signature (bits 63 to 32).
    public uint word2;    //Word 2 of 128-bit signature (bits 95 to 64).
    public uint word3;    //Word 3 of 128-bit signature (bits 127 to 96).
}

private void softSig(ref byte[] data, uint length, ref FLASH_SIG_Type pSig)
{
    FLASH_SIG_Type flashWord;
    FLASH_SIG_Type refSignature;
    refSignature.word0 = 0;
    refSignature.word1 = 0;
    refSignature.word2 = 0;
    refSignature.word3 = 0;
    FLASH_SIG_Type nextSign;

    uint PageAddr = 0;
    for (uint i = 0; i < (length >> 4); i++) {
        flashWord.word0 = (uint)data[PageAddr++] + ((uint)data[PageAddr++] << 8) + ((uint)data[PageAddr++] << 16) + ((uint)data[PageAddr++] << 24);
        flashWord.word1 = (uint)data[PageAddr++] + ((uint)data[PageAddr++] << 8) + ((uint)data[PageAddr++] << 16) + ((uint)data[PageAddr++] << 24);
        flashWord.word2 = (uint)data[PageAddr++] + ((uint)data[PageAddr++] << 8) + ((uint)data[PageAddr++] << 16) + ((uint)data[PageAddr++] << 24);
        flashWord.word3 = (uint)data[PageAddr++] + ((uint)data[PageAddr++] << 8) + ((uint)data[PageAddr++] << 16) + ((uint)data[PageAddr++] << 24);

        //Update 128 bit signature
        nextSign.word0 = flashWord.word0 ^ refSignature.word0 >> 1 ^ refSignature.word1 << 31;
        nextSign.word1 = flashWord.word1 ^ refSignature.word1 >> 1 ^ refSignature.word2 << 31;
        nextSign.word2 = flashWord.word2 ^ refSignature.word2 >> 1 ^ refSignature.word3 << 31;
        nextSign.word3 = flashWord.word3 ^ refSignature.word3 >> 1 ^
                         (refSignature.word0 & 1 << 29) << 2 ^
                         (refSignature.word0 & 1 << 27) << 4 ^
                         (refSignature.word0 & 1 << 2) << 29 ^
                         (refSignature.word0 & 1 << 0) << 31;

        //Point to the calculated value
        refSignature.word0 = nextSign.word0;
        refSignature.word1 = nextSign.word1;
        refSignature.word2 = nextSign.word2;
        refSignature.word3 = nextSign.word3;
    }

    //Copy the reference signature to the result pointer
    pSig.word0 = refSignature.word0;
    pSig.word1 = refSignature.word1;
    pSig.word2 = refSignature.word2;
    pSig.word3 = refSignature.word3;
}
02 Oct 2013

Code is now up! This should work on any mbed-enabled microcontroller, although I was only able to test it on the LPC11U24 and KL25Z:

[Repository '/users/neilt6/code/FlashSig_HelloWorld/latest/' not found]

I've also written improved C# code for generating reference signatures:

public struct FLASH_SIG_Type {
    public uint word0;    //Word 0 of 128-bit signature (bits 31 to 0).
    public uint word1;    //Word 1 of 128-bit signature (bits 63 to 32).
    public uint word2;    //Word 2 of 128-bit signature (bits 95 to 64).
    public uint word3;    //Word 3 of 128-bit signature (bits 127 to 96).
}

private FLASH_SIG_Type generate(ref byte[] data)
{
    //Initialize the local variables
    FLASH_SIG_Type refSignature = new FLASH_SIG_Type();
    FLASH_SIG_Type nextSignature;
    FLASH_SIG_Type dataWord;

    //The initial data index
    uint dataIndex = 0;

    //Calculate the signature for the specified region
    for (uint i = 0; i < (data.Length >> 4); i++) {
        //Load the next 128-bit data word
        dataWord.word0 = (uint)data[dataIndex++] + ((uint)data[dataIndex++] << 8) + ((uint)data[dataIndex++] << 16) + ((uint)data[dataIndex++] << 24);
        dataWord.word1 = (uint)data[dataIndex++] + ((uint)data[dataIndex++] << 8) + ((uint)data[dataIndex++] << 16) + ((uint)data[dataIndex++] << 24);
        dataWord.word2 = (uint)data[dataIndex++] + ((uint)data[dataIndex++] << 8) + ((uint)data[dataIndex++] << 16) + ((uint)data[dataIndex++] << 24);
        dataWord.word3 = (uint)data[dataIndex++] + ((uint)data[dataIndex++] << 8) + ((uint)data[dataIndex++] << 16) + ((uint)data[dataIndex++] << 24);

        //Calculate the word's signature using the previous signature as a reference
        nextSignature.word0 = dataWord.word0 ^ refSignature.word0 >> 1 ^ refSignature.word1 << 31;
        nextSignature.word1 = dataWord.word1 ^ refSignature.word1 >> 1 ^ refSignature.word2 << 31;
        nextSignature.word2 = dataWord.word2 ^ refSignature.word2 >> 1 ^ refSignature.word3 << 31;
        nextSignature.word3 = dataWord.word3 ^ refSignature.word3 >> 1 ^
                              (refSignature.word0 & 1 << 29) << 2 ^
                              (refSignature.word0 & 1 << 27) << 4 ^
                              (refSignature.word0 & 1 << 2) << 29 ^
                              (refSignature.word0 & 1 << 0) << 31;

        //The calculated signature is the new reference signature
        refSignature.word0 = nextSignature.word0;
        refSignature.word1 = nextSignature.word1;
        refSignature.word2 = nextSignature.word2;
        refSignature.word3 = nextSignature.word3;
    }

    //Return the reference signature
    return refSignature;
}