Program hangs with libopencm3 on STM32F103C8T6 (Blue-Pill)

victor-lamoine wrote on Wednesday, March 20, 2019:

Hello,

I’m trying to learn / use FreeRTOS with libopencm3 on a STM32 thanks to Warren
Gay’s book
the code examples used in his book are available here
I have already asked this question on StackOverflow and on ve3wwg/stm32f103c8t6 but I did not get an answer that solved my problem.

Hardware

  • MCU: STM32F103C8T6 (also known as Blue Pill)
  • FT232RL breakout board
  • ST-Link V2

Software

  • Kubuntu 18.04
  • arm-none-eabi-gcc (15:6.3.1+svn253039-1build1) 6.3.1 20170620
  • gdb-multiarch - GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2)
  • FreeRTOS 10.2.0
  • libopencm3 latest commit 0fd4f74ee301af5de4e9b036f391bf17c5a52f02

The following program creates two tasks and a queue. The queue is filled by the task “SEND” and the task “RECEIVE” receives from the same queue, the Blue Pill LED (GPIOC, GPIO13) is turned on by the “SEND” task every 1100 ms and the “RECEIVE” tasks turns it off when it receives a message (100 ms after it has been turned on).

#include <FreeRTOS.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <queue.h>
#include <task.h>

static QueueHandle_t queue;

static void
task_receive(void *args __attribute__((unused)))
{
  bool nothing;
  while (1)
  {
    if (xQueueReceive(queue, &nothing, 10) == pdPASS)
      gpio_set(GPIOC, GPIO13); // Turn off
  }
}

static void
task_send(void *args __attribute__((unused)))
{
  bool nothing = false;
  while (1)
  {
    gpio_clear(GPIOC, GPIO13); // Turn on
    vTaskDelay(pdMS_TO_TICKS(100));
    xQueueSendToBack(queue, &nothing, portMAX_DELAY);
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

int
main(void)
{
  rcc_clock_setup_in_hse_8mhz_out_72mhz();

  // Blue-Pill led
  rcc_periph_clock_enable(RCC_GPIOC);
  gpio_set_mode(
    GPIOC,
    GPIO_MODE_OUTPUT_2_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL,
    GPIO13);
  gpio_set(GPIOC, GPIO13); // Turn off (polarity of the led is inversed!)

  queue = xQueueCreate(32, sizeof(bool));
  if (queue == 0)
  {
    while (1)
    {
      gpio_toggle(GPIOC, GPIO13);
      for (uint32_t i = 0; i < 80000; ++i)
        __asm__("nop");
    };
  }
  xTaskCreate(task_receive, "RECEIVE", 200, NULL, configMAX_PRIORITIES-1, NULL);
  xTaskCreate(task_send, "SEND", 200, NULL, configMAX_PRIORITIES-2, NULL);
  vTaskStartScheduler();
  while(1);
  return 0;
}

This program does not work when I flash it on a Blue Pill, the led stays on and never turns off, I think the execution is blocking at the xQueueSendToBack call (it never exits).

I am using the configuration files from the rtos/uart2 example of the book.

I have tried debugging this with the ST-Link V2, here is the output of my debug session: (note: the program is named uart.elf because I’m using the rtos/uart2 example files, I replaced the code with the one above)

gdb-multiarch
layout split
file uart.elf
target extended-remote :4242
load uart.elf
b main

At that point the everything looks fine from my point of view: gdb session

Then I set a breakpoint to the xQueueSendToBack call and continue:

(gdb) b uart.c:30
Breakpoint 2 at 0x8000156: file uart.c, line 30.
(gdb) c
Continuing.

If I let the code continue and halt it afterwards I get this:

(gdb) c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
0x08000e9c in prvIdleTask (pvParameters=<optimized out>) at rtos/tasks.c:3166
(gdb) bt
#0  0x08000e9c in prvIdleTask (pvParameters=<optimized out>) at rtos/tasks.c:3166
#1  0x08000462 in uxListRemove (pxItemToRemove=<optimized out>) at rtos/list.c:238
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

There seems to be something wrong here, however I don’t know how to interpret this corrupt stack error.
Why is this example not working on my Blue-Pill? There must be something wrong in my setup but I can’t find what.

If there is any information missing please ask as I’m willing to provide as much as you need to help me fix this problem. I’m a little desperate here :slight_smile:

Bye

rtel wrote on Wednesday, March 20, 2019:

It is hard to see the context using the command line debugger, but it is
quite normal to be in the idle task when you stop the debugger,
especially in your application with only two asks that are blocked most
of the time.

The fact that you are in the idle task would seem to indicate that the
scheduler has started. The fact that GDB doesn’t know how to decode the
stack can just be because there is no real end to the stack as far as
GDB knows because the task stacks from from the heap (unless you are
using xTaskCreateStatic() to create the tasks). Using configuration
options it is possible to try an alleviate the debuggers interpretation
of the stack by ending it with a NULL value.

First thing then, are you sure the LED is working as you expect? If you
were to ignore FreeRTOS for a moment and just try turning the LED on and
off from main() before the scheduler is started, does it turn on and off
as expected?

Next thing to do would be to check the tick interrupt is executing.
When you are stopped on the debugger try viewing the xTickCount
variable. If it is not incrementing then time is standing still as far
as the scheduler is concerned so the blocked tasks will never time out.

victor-lamoine wrote on Wednesday, March 20, 2019:

I forgot to mention that FreeRTOS tasks without queues work fine and so does libopencm3 at toggling the LED:

#include <FreeRTOS.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include <task.h>

static void
task_off(void *args __attribute__((unused)))
{
  while (1)
  {
    vTaskDelay(pdMS_TO_TICKS(300));
    gpio_set(GPIOC, GPIO13); // Turn off
  }
}

static void
task_on(void *args __attribute__((unused)))
{
  while (1)
  {
    vTaskDelay(pdMS_TO_TICKS(1000));
    gpio_clear(GPIOC, GPIO13); // Turn on
  }
}

int
main(void)
{
  rcc_clock_setup_in_hse_8mhz_out_72mhz();

  // Blue-Pill led
  rcc_periph_clock_enable(RCC_GPIOC);
  gpio_set_mode(
    GPIOC,
    GPIO_MODE_OUTPUT_2_MHZ,
    GPIO_CNF_OUTPUT_PUSHPULL,
    GPIO13);
  gpio_set(GPIOC, GPIO13); // Turn off (polarity of the led is inversed!)

  xTaskCreate(task_on, "ON", 200, NULL, configMAX_PRIORITIES-1, NULL);
  xTaskCreate(task_off, "OFF", 200, NULL, configMAX_PRIORITIES-1, NULL);
  vTaskStartScheduler();
  while(1);
  return 0;
}

This code works fine and the LED blinks (with a hard to predict scheme, but that’s not the point).
Things stop working when I push a value into a queue.


For the first program (the one with the queue), just before the call to xQueueSendToBack the tick count (xTickCount) is 0, after that it is equal to 100 and never changes after that, I have continued/halted the program several times.

This time it looks like the program was stuck in the blocking_handler(void) function of vector.c in libopencm3: https://github.com/libopencm3/libopencm3/blob/master/lib/cm3/vector.c#L102

victor-lamoine wrote on Thursday, March 21, 2019:

I have investigated further.

The problem I experience lays in the prvCopyDataToQueue function of the queue.c file, I’m now pretty sure that the memcpy call at line 2098 (of queue.c) seems to be the root cause of my problem.

This is a portion of the surrounding code (line 2096 of queue.c):

	else if( xPosition == queueSEND_TO_BACK )
	{
		( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0.  Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */
		pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */
		if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */
		{
			pxQueue->pcWriteTo = pxQueue->pcHead;
		}

I’m using rtos/uart2 example . At the beginning of the prvCopyDataToQueue function everything looks fine:

Breakpoint 2, prvCopyDataToQueue (pxQueue=pxQueue@entry=0x20000020 <ucHeap+8>,
    pvItemToQueue=pvItemToQueue@entry=0x8001698, xPosition=xPosition@entry=0) at rtos/queue.c:1698
1: pxQueue->pcWriteTo = (int8_t *) 0x20000068 <ucHeap+80> ""
2: pxQueue->pcTail = (int8_t *) 0x20000168 <ucHeap+336> ""
3: pxQueue->uxItemSize = 1

The queue is empty and the queue size in this example is 256, we have ucHeap+336 - ucHeap+80 = 256, all good!

Next breakpoint is at the memcpy call, after the memcpy call this is what I get:

1: pxQueue->pcWriteTo = (int8_t *) 0x8001603 <null_handler> "G"
2: pxQueue->pcTail = (int8_t *) 0x8001605 <reset_handler> "\"8\265\030I\031L\031HS\030\203B", <incomplet
e sequence \323>
3: pxQueue->uxItemSize = 134223361

The item size becomes gigantic and the memcpy did nasty things to the queue, the adresses are completely wrong.

Any idea why?

victor-lamoine wrote on Thursday, March 21, 2019:

I solved the issue by updating the compiler to the latest version found on https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads: Version 8-2018-q4-major Linux 64-bit

After this update and recompiling the example code everything executes as expected!

So I recommend not to use the arm-none-eabi compiler shipped with Kubuntu 18.04:
arm-none-eabi-gcc (15:6.3.1+svn253039-1build1) 6.3.1 20170620. It seems to contain a bug in memcpy for cortex M3 targets.

Thanks for you support.

rtel wrote on Thursday, March 21, 2019:

Thanks for taking the time to report back.