5

I have 2 pieces of code, which do the exact same thing, but one does not actually work. Can anyone explain why?

The code is sending data via spi to an FPGA running the display. I'm almost out of code storage on the chip, so I was trying to cut down as much as I could. The change below ended up breaking for some reason, the rest of the program is exactly the same as it.

//Looping to execute code twice doesnt work
for (byte i = 0; i < 3; i++)
      {
        temp2 = temp % 10;
        temp /= 10;
        temp2 |= 0x40;
        for (byte k = 0; k < 2; k++)
        {
          SPI.transfer(reg[j]);
          delayMicroseconds(10);
          SPI.transfer(temp2);
          delayMicroseconds(10);
        }
        reg[j] -= 1;
      }

.

//But copy-paste does
for (int i = 0; i < 3; i++)
          {
            temp2 = temp % 10;
            temp /= 10;
            temp2 |= 0x40;
            SPI.transfer(reg[j]);
            delayMicroseconds(10);
            SPI.transfer(temp2);
            delayMicroseconds(10);
            SPI.transfer(reg[j]);
            delayMicroseconds(10);
            SPI.transfer(temp2);
            delayMicroseconds(10);
            reg[j] -= 1;
          }
  • 3
    The inner `k` loop may be introducing delays after the 4th transfer due to loop overhead (print the assembly language). Although the two examples are *functionally* equivalent, their timings are different. – Thomas Matthews Jan 06 '17 at 20:27
  • 1
    Your compiler may be optimizing out something in the inner-for loop which is causing issues. Try adding "-O0" to your compiler arguments and see if that helps. – user2205930 Jan 06 '17 at 20:32

3 Answers3

0

The most likely explanation is that some other code is relying on this loop meeting specific timing constraints, and failing if it doesn't.

The changes you have introduced that potentially affect timing include;

  • Changing i to be of type byte rather than int. This can affect timing - int is normally the "native" type, for which operations are more efficient by various measures. It is possible that using a byte changes the timing of the outer loop. For example, if byte is a smaller type than an int, operations on a byte may involve conversion to and from int).
  • Essentially you've replaced a repeated sequence of statements with an inner loop. Depending on optimisation settings, the compiler might unroll the loop (producing, in effect, the same behaviour as the first code sample), or it may not. If the compiler does not unroll the loop, the overhead of the loop construct itself (initialising the variable k, checking and incrementing it on each iteration, etc) can affect timing of the code.

If there is some reliance on this code meeting a specific timing constraint, this needs to be documented somewhere. If the requirement is not documented, I would suggest you document it (e.g. enter it as a specific requirement) and then document derived requirements (e.g. one requirement to use int to control the outer loop [if that is needed] and another requirement that the inner loop be unrolled in code rather than relying on compiler optimisation).

Peter
  • 35,646
  • 4
  • 32
  • 74
  • with all those `delayMicroseconds(10);` I doubt timing of some byte to int conversions or loops instead of consecutive execution makes any difference... – datafiddler Jan 06 '17 at 23:29
  • That depends on sensitivity of other parts of the system (e.g. the FPGA driving the display) to variations in timings, how rigidly timing restrictions are enforced, and basic things like clock speed (embedded systems can vary anywhere between fractions of a Hertz through to GHz [which you are assuming]). – Peter Jan 07 '17 at 03:29
0

... breaking for some reason ...

is never nice, and if something "breaks", it can have different affects on different sketches.

reg[j] -= 1; 

If e.g. this hurts, because j is out of bounds, it might have different effects whether there is a variable k or not.

Try to isolate the issue and make it reproducible ... I bet the problem is not in the posted part. ;)

datafiddler
  • 1,755
  • 3
  • 17
  • 30
  • Unless a VERY old pre-standard C++ compiler is being used, the variable `k` no longer even exists during the statement `reg[j] -= 1`, since it has passed out of scope. – Peter Jan 07 '17 at 03:24
  • Correct. If a local variable on the stack is out of scope (made legally inaccessible for compiled code) you can modify it by such undetectable out-of bounds statements and won't notice an issue. If there is no such variable at all, the same bad statement has a different effect and might hurt somewhere else. – datafiddler Jan 26 '17 at 14:32
0

In your original post: "... I'm almost out of code storage on the chip ...". How are you doing with your RAM? The one time I have seen something like that in my own code was when I simply run out of RAM and my variables were corrupting the stack or the stack was corrupting my variables. Remember that C/C++ needs some 'working' memory to keep track of everything. Your variables are assigned from one end of your available RAM and the stack from the other end. When you use too much RAM or your functions are nested too deeply, the two collide and interfere/corrupt each other.

|************ Available RAM ************|
| Variables >>>>>>>>>>>>>>>>!!<<< Stack |

Variables grow >
Stack grow <
Problem !!

RobertW
  • 26
  • 3