AVR-GCC for Linux doesn't handle well 16 bit words

No, you don't, and I'm compelled to give you all details:

Such program is built under a project called Molcajete, that integrates Arduino, FreeRTOS (real time operating system) and Arduino-mk (for avoiding sketches and compiling from the command line, as it should be).

You might download it (but don't install it) along the file heap_3.c. Look for /your/molcaje/installation/libraries/FreeRTOS/src/ and copy the file heap_3.c here, and delete the file heap_1.c

Then you might try the code in this repo. Before building make sure that the first line in the Makefile points to your Molcajete installation. For example, mine is:

ARDUINO_DIR = /home/fjrg76/arduino-1.8.13

and make sure the last line in the Makefile points to your Arduino.mk. For example, mine is:

include /home/fjrg76/arduino-1.8.13/Arduino.mk

Now open a terminal pointing where the files are and execute the make utility:

$ make

In order to replicate my problem you'll need an Arduino UNO board.

For uploading the program type in the terminal:

$make upload

You can use the serial monitor that is included in your Arduino installation, or you might try it in the same terminal:

$make monitor

(For exiting type Ctrl-a + k + y)

Hope you can replicate my problem. Greetings!

Good luck @fjrg76. I suspect you are going to need it.

I'll leave you with this parting thought: The posted assembly proves your title is fantastically wrong. What else have you gotten wrong?

It isn't. The problem is that comparing words seems to have issues. Anyway, thank you for your time.

Can somebody show the assembly code for this loop? Just for comparison with the TO code.

Or tell me how to obtain assembly code in the Arduino IDE.

Bugs are often in parts of code you believe to be unrelated/flawless. Since a compiler bug is unlikely suspects are the mysterious function or the kernel. If it works with now++; and fails with the function the chances there is something wrong with the function would be great.

You say it "crashes" with Serial.print 32-bit value. What else is corrupted in your setup?

The problem is, your suggested method of replication is not minimal. A test sketch (or functional module) that is not minimal, leaves open too many hypothetical causes. This makes it difficult to analyze.

The only other way to prove that a problem exists, is to reveal the problem at its source, for example to find the lines in the core and identify some commonly understood flaw that would cause the error in the application code. You have not done that. You have only pointed a finger "in the air" as it were.

As it stands, there is a reasonable chance that the problem exists somewhere in your code. Nobody will take you seriously until you find the actual reason why there is a problem, and demonstrate clearly that it is exhibited only by the compiler, and definitely not your code.

A minimal sketch could prove the problem without necessarily identifying the cause, but you haven't provided it. Without that, you are placing an unfair burden on the compiler maintainers, because they have to plow through and understand all your code as well as the compiler code. They also know, statistically, more often than not, a claimed compiler bug is actually an application bug.

Hi,

I'm a seasoned programmer (I like to think that), and when I ask for help is because I run out of ideas; otherwise I didn't. I program embedded systems professionally and I cannot deliver a product that works half of the time.

If I knew the reason then I would've look for the solution myself. In this case the assembly output seems to be ok, but the issue shows there's something wrong, that's why I'm here asking for other eyes. Unfortunately it seems that nobody compiles from the command line as I do, which makes harder to find the reason of such misbehaviour.

The program is minimal, the setup isn't. Sadly there's not a better way to replicate the problem.

This isn't the first issue I've ever found with avr-gcc, actually this is the third.

Anyway I really appreciate your time and effort and from here I'm on my way. As soon as I get the solution I'll let you know it (if any).

Thank you all!

Since there is .elf file in the temporary folder when compiled, you can get it with avr-objdump.exe.

That is an experiment you could perform to eliminate one possibility at least, if you compile it in an IDE. The IDE uses the same compiler so you would find out if your compilation process has a flaw.

I'm roundabout asking how much of your compilation process is due to your preferences, or mandatory, or optional... so if you can eliminate as many non-standard elements as possible.

If you look at the assembly output you might say it's correct, but when the program is running, then it's not ok.

So that'd be impossible, right? Look for errors in your RTOS, somehow not preserving some register under some circumstances?

Does it ALWAYS fail for values above 32767, or only sometimes.

it seems that nobody compiles from the command line as I do

So in addition to a non-standard environment, you have a non-standard build process as well?!

What happens if you replace xTaskGetTickCount() with something deterministic:

TickType_t TickValues = [2598, 2602, 6603, 8603, 30680, ...];
TickType_t xTaskGetTickCount() {
  static count i=0;
  if (++i >= sizeof(TickValues)/sizeof(TickValues[0]))
    i = 0;
  return TickValues[i];
}

Can you package up your entire project on github? This whole "get x from a, y from b, delete z and replace it with c" is really far from "easily reproducible."
Include a script to do the build.

Also, it might be helpful if you uploaded the .elf file of your test...

Is there a 16 bit AVR ?
I’d suspect that the “critical section” macro is not functioning as you expect and replace it with a cli() sei() block as a test. That macro appears itself to be dependent on the correct configuration of other macros (I had a quick look at some similar STM32 code)

Just to emphasize, in case it is not clear, that the symptoms here appear to be consistent with non atomic reading of multi byte variables by an 8 bit MCU, hence the suggestion to force an interrupt suspension during the read operation. The macro portTICK_TYPE_ENTER_CRITICAL() appears to be configured in such a way that it does not act if the protected operation is identified as atomic because of a processor bit width setting.

That may not be as deterministic as you would like due to the way sizeof has been used.

Fixed.

You can hardly get something more deterministic than incrementing by one each iteration. Yet the OP refuses to try it. Or is unable to replicate the problem? We don't know.

I'd be interested to see that MWE for that one.

Trying to duplicate the code as closely as possible on an Arduino UNO the results are as expected: Fine through 32767-32768 and counts very quickly when next_wakeup rolls over near 65535.

//in another file:
#define configUSE_16_BIT_TICKS 1

#if( configUSE_16_BIT_TICKS == 1 )
  typedef uint16_t TickType_t;
  #define portMAX_DELAY ( TickType_t ) 0xffff
#else
  typedef uint32_t TickType_t;
  #define portMAX_DELAY ( TickType_t ) 0xffffffffUL
#endif

TickType_t xTaskGetTickCount( void )
{
    TickType_t xTicks = (TickType_t) millis();

    return xTicks;
}

void software_delay(TickType_t ticks)
{
  volatile TickType_t now = (TickType_t) xTaskGetTickCount();

  Serial.print( "Before: " );
  Serial.print(now);

  volatile TickType_t next_wakeup = now + ticks;
  
  Serial.print( "\tNext: " );
  Serial.print( next_wakeup );

  while ( 1 )
  {
    if (now > next_wakeup)
      break;
    now = xTaskGetTickCount();
  }

  Serial.print( "\tAfter: " );
  Serial.println(now);
}

void setup()
{
  Serial.begin(115200);
  delay(200);
}

void loop()
{
  software_delay(1000);
}

Did you try this also with volatile variables and get a different result ? I'm guessing that you have simply made the assumption that because you got reasonable results without using volatile variables that the cause of the problem the OP reported may be the use of volatile variables.
It would certainly be worth investigating if, with a relatively simple sketch, volatile variables somehow caused a problem.

I have tried also with volatile local variables (see reply 37) but I have not been able to reproduce the problem described by the OP. The result I got was the result I expected: the same result I got with non-volatile local variables.

It appears that nobody but the OP can reproduce the OP's described problem (overflow at 32768). By suggesting that volatile local variables were unusual, I hoped to give the OP something to try.

If the OP can make a change that eliminates their described problem then they will be a lot closer to determining if there is a compiler bug they can report.

1 Like

If you suspect a compiler bug, why use such an ancient version of the compiler? The latest version is GCC 11.2.

99% of the times that people blame a compiler bug, there's some other problem in their code.
There is either something seriously wrong with how you're building the project, or you're invoking undefined behavior. This issue, not being able to print 32-bit integers and not being able to left-shift integers are symptoms of a much more serious problem, if these were issues with the compiler, we would have known by now.

FWIW, avr-gcc 5.4.0 generates sensible code: Compiler Explorer

#include <stdint.h>

using TickType_t = uint16_t;
TickType_t xTaskGetTickCount();

void software_delay(TickType_t ticks) {
  TickType_t now = xTaskGetTickCount();
  TickType_t next_wakeup = now + ticks;

  while (1) {
    if (now > next_wakeup)
      break;
    now = xTaskGetTickCount();
  }
}
software_delay(unsigned int):
        push r28
        push r29
        movw r28,r24
        call xTaskGetTickCount()
        add r28,r24
        adc r29,r25
.L3:
        cp r28,r24
        cpc r29,r25
        brlo .L1
        call xTaskGetTickCount()
        rjmp .L3
.L1:
        pop r29
        pop r28
        ret

By using volatile you force the compiler to reserve space on the stack and actually perform any loads and stores to these local variables, which is completely unnecessary. But even in that case, the generated code looks correct.

This isn't to say that the final binary will contain the exact same code, especially if you have LTO turned on, but I think a compiler bug is really unlikely.