cembeddedmicrocontroller

An extremely peculiar compiler issue


I've written code for PIC16F1947 in C. I am using the following:

A part of the code deals with data coming from PC. The specific packet I'm sending from PC is 325 bytes. This packet looks as follows:

data:  0, 64, 1, 0, 255, 255, 255, ... (all 255) ..., 255,   1
index: 0   1  2  3    4    5    6                     323  324

The contents of the packet are shown as 8 bit decimal value (8 bit unsigned integer). The micro stores it in an array of unsigned char:

unsigned char _command_mgr_buff[330];

unsigned char is the 8 bit unsigned integer for PIC16F.

The last byte of the packet, ie index 324, is the checksum of the packet. It is the summation of index 1 through 323, including 1 and 323. The PC code (in C#) that calculates this checksum is as follows:

allCertPages[324] = 0;
for (int i = 1; i <= 323; i++)
{
    allCertPages[324] += allCertPages[i];
}

allCertPages is a byte[].

The micro must verify that the checksum is indeed the value passed from PC. Here is the verification code that I've written for PIC16F, including some debug information:

param0 = _command_mgr_buff[324]; // param0 is unsigned int, 16 bit
param1 = _command_mgr_buff[324]; // param1 is unsigned int, 16 bit

// Checksum verification
for (var = 1; var <= 323; var++) // var is unsigned int, 16 bit
{
    _command_mgr_buff[324] -= _command_mgr_buff[var];
    param1 -= _command_mgr_buff[var];
}

if (!_command_mgr_buff[324])
{
    send_status(CS_BAD_PARAM);
}

The idea is to subtract all values within the range [1, 323] from the checksum. If the final value is 0, then checksum is correct. Otherwise, if _command_mgr_buff[324] is found non-zero after subtraction, then the checksum is incorrect.

After executing the code in debug mode (and also in release mode), I'm getting non-zero value in _command_mgr_buff[324] (so send_status(CS_BAD_PARAM); gets executed and the PC thinks something is wrong), but zero in the lower byte of param1!

How is that possible?!

In case you're interested, here is the assembly generated for the non-zero checking:

  8780                           ;mgr_command.c: 1230: if (!_command_mgr_buff[324])
  8781  0E89  30EA                  movlw   low(8870+0144h)
  8782  0E8A  00D3                  movwf   (??_command_mgr_run+0)^080h+0
  8783  0E8B  3023                  movlw   high(8870+0144h)
  8784  0E8C  00D4                  movwf   (??_command_mgr_run+0)^080h+0+1
  8785  0E8D  0853                  movf    0+(??_command_mgr_run+0)^080h+0,w
  8786  0E8E  0086                  movwf   fsr1l
  8787  0E8F  0854                  movf    1+(??_command_mgr_run+0)^080h+0,w
  8788  0E90  0087                  movwf   fsr1h
  8789                           
  8790  0E91  0881                  movf    indf1,f
  8791  0E92  1D03                  skipz
  8792  0E93  2E95                  goto    u10101
  8793  0E94  2E96                  goto    u10100
  8794  0E95                     u10101:
  8795  0E95  2E9B                  goto    l55070
  8796  0E96                     u10100:
  8797                              line    1232
  8798                              
  8799  0E96                     l55068:    
  8800                           ;mgr_command.c: 1231: {
  8801                           ;mgr_command.c: 1232: send_status(0x11);
  8802  0E96  3011                  movlw   (011h)
  8803  0E97  31B6  2693  3188      fcall   _send_status
  8804                              line    1233
  8805                           ;mgr_command.c: 1233: }
  8806  0E9A  2FE3                  goto    l45048
  8807                              line    1234

Here is a screenshot taken during debugging. Please check the Watch window in the right and the tooltip for 324-th element.

PIC16F Debug


Solution

  • If your code is as you say, the main problem here is your test itself:

    if (!_command_mgr_buff[324])
    {
        send_status(CS_BAD_PARAM);
    }
    

    You're sending the signal if the result is what you want (namely zero)...

    This is one of the reasons why I don't like boolean testing of non-boolean values. Boolean variables often have names that cue you into reading the condition they capture correctly, like if (isLoaded) or if (!anyErrors)...and that doesn't hold for numerics. So it's easier to make sure you've got the semantics you want to just write it out:

    if (_command_mgr_buff[324] != 0) 
    {
        send_status(CS_BAD_PARAM);
    }
    

    As for why your debugger is giving you the 0x0F...can't help you there...my point just being that you're in the zero branch. You might try a clean build and see if it still says that. (The program's debug symbols could be out of date or something?)