Skip to content

Concepts First

  • POSIX: a standard set of OS interfaces (pthread threads, semaphores, message queues, etc.). These are the APIs used for multithreaded programming on Linux.
  • Compatibility layer: makes the embedded system "pretend" to be Linux — POSIX APIs implemented on top of FreeRTOS, so code written on a PC can be ported to the MCU.
  • pthread: POSIX threads. pthread_create creates a thread, pthread_mutex_lock/unlock lock/unlock, pthread_cond_wait/signal are condition variables, pthread_barrier_wait synchronizes groups.
  • POSIX semaphores & message queues: sem_open/sem_wait/sem_post and mq_open/mq_send/mq_receive are the standard interfaces for synchronization and data passing between tasks.
  • Relation to native FreeRTOS APIs: underneath, the compatibility layer still uses FreeRTOS tasks/queues/semaphores; only the "skin" is POSIX. Which to use depends on preference and portability needs.

Example Overview

This page is based on the freertos_posix example in the official Bouffalo SDK (examples/posix/freertos_posix), which demonstrates the FreeRTOS POSIX compatibility layer:

  • The example is a POSIX API test suite: 9 groups covering pthread (create/attr/mutex/cond/barrier), semaphores, message queues, clock/timer, and sched/unistd;
  • Shell commands run all tests (posix_test) or a single group (posix_test_single <group>);
  • Each case counts run/pass/fail/skip, and print_test_summary() prints the totals;
  • With CONFIG_POSIX=y, your project can #include <pthread.h>, <semaphore.h>, <mqueue.h> directly.

Note

This page has little code but dense concepts: to write your own multithreaded code, first run posix_test, then study the test cases under test/.

Operation Steps

1
Enter the Example Directory

Open a terminal and enter the SDK POSIX compatibility example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/posix/freertos_posix
2
Check the POSIX Config

The example defconfig already contains CONFIG_POSIX=y; if you start a new project, add this line to defconfig or enable POSIX support in make menuconfig and save.

grep CONFIG_POSIX defconfig
3
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
4
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
Run and Verify

Open a serial tool (baud rate 2000000). At the bouffalolab /> prompt, type posix_test and press Enter: the suite runs 9 test groups (pthread, pthread_attr, mutex, cond, barrier, sem, mqueue, clock, sched) and prints a PASSED/FAILED summary. Use posix_test_single pthread to run one group.

Code Execution Flow

The flow from boot to completing the tests is:

APIs Used by the Example

pthread_create / pthread_join / pthread_attr_*(...)

Creates/joins threads and configures thread attributes (detach state, stack size, scheduling parameters). Used by test/test_pthread.c and others.

Parameters:

  • thread: output thread ID
  • attr: thread attributes (or NULL for defaults)
  • start_routine / arg: thread function and argument

Returns: 0 on success; an error code on failure

pthread_mutex_lock / pthread_mutex_unlock(mutex)

Locks/unlocks a mutex to protect shared data. The tests also cover attributes (pthread_mutexattr_*) and the timed variant (pthread_mutex_timedlock).

Parameters:

  • mutex: the mutex object

Returns: 0 on success; an error code on failure

sem_open / sem_wait / sem_post(...)

Creates a named semaphore and performs P/V operations (wait/release) for task synchronization.

Parameters:

  • name: semaphore name (e.g., /sem0)
  • value: initial value
  • sem: semaphore pointer

Returns: 0 on success; -1 on failure with errno set

mq_open / mq_send / mq_receive(...)

Creates a message queue and sends/receives messages — the standard way to pass prioritized data between tasks.

Parameters:

  • name: queue name
  • attr: queue attributes (length, message size)
  • msg / prio: message data and priority

Returns: mqd_t descriptor / bytes sent on success; (mqd_t)-1 on failure

Complete Code

The following is the complete source of posix/freertos_posix/main.c, identical to the official example, collapsed by default (test groups live in the test/ subdirectory):

📜 Click to expand posix/freertos_posix/main.c full code
c
#include <stdio.h>
#include <string.h>
#include "board.h"
#include "shell.h"
#include "bflb_core.h"
#include <FreeRTOS.h>
#include "task.h"
#include "test_common.h"

/* Forward declaration for shell_init_with_task (not declared in shell.h) */
extern void shell_init_with_task(struct bflb_device_s *uart);

/* Stack overflow hook - called when FreeRTOS detects stack overflow */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    printf("\r\n[STACK OVERFLOW] Task: %s\r\n", pcTaskName);
    while (1) {
        /* Halt to allow debugging */
    }
}

/* Global test counters */
int g_tests_run = 0;
int g_tests_passed = 0;
int g_tests_failed = 0;
int g_tests_skipped = 0;

/* External declarations for each test group */
extern void test_pthread_run(void);
extern void test_pthread_attr_run(void);
extern void test_pthread_mutex_run(void);
extern void test_pthread_cond_run(void);
extern void test_pthread_barrier_run(void);
extern void test_semaphore_run(void);
extern void test_mqueue_run(void);
extern void test_clock_timer_run(void);
extern void test_sched_unistd_run(void);

/* Run all tests */
static void run_all_tests(void)
{
    printf("\r\n===== POSIX API Test Suite Start =====\r\n\r\n");

    /* Reset counters */
    g_tests_run = 0;
    g_tests_passed = 0;
    g_tests_failed = 0;
    g_tests_skipped = 0;

    printf("--- 1. pthread tests ---\r\n");
    test_pthread_run();

    printf("\r\n--- 2. pthread_attr tests ---\r\n");
    test_pthread_attr_run();

    printf("\r\n--- 3. pthread_mutex tests ---\r\n");
    test_pthread_mutex_run();

    printf("\r\n--- 4. pthread_cond tests ---\r\n");
    test_pthread_cond_run();

    printf("\r\n--- 5. pthread_barrier tests ---\r\n");
    test_pthread_barrier_run();

    printf("\r\n--- 6. semaphore tests ---\r\n");
    test_semaphore_run();

    printf("\r\n--- 7. mqueue tests ---\r\n");
    test_mqueue_run();

    printf("\r\n--- 8. clock/timer tests ---\r\n");
    test_clock_timer_run();

    printf("\r\n--- 9. sched/unistd tests ---\r\n");
    test_sched_unistd_run();

    print_test_summary();
}

/* Shell command: run all tests */
static void cmd_posix_test(int argc, char **argv)
{
    /* Run tests directly in shell task context */
    run_all_tests();
}
SHELL_CMD_EXPORT_ALIAS(cmd_posix_test, posix_test, Run all POSIX tests);

/* Shell command: run single test group */
static void cmd_posix_test_single(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: posix_test_single <group>\r\n");
        printf("Groups: pthread, pthread_attr, mutex, cond, barrier, sem, mqueue, clock, sched\r\n");
        return;
    }

    g_tests_run = 0;
    g_tests_passed = 0;
    g_tests_failed = 0;
    g_tests_skipped = 0;

    if (strcmp(argv[1], "pthread") == 0) {
        test_pthread_run();
    } else if (strcmp(argv[1], "pthread_attr") == 0) {
        test_pthread_attr_run();
    } else if (strcmp(argv[1], "mutex") == 0) {
        test_pthread_mutex_run();
    } else if (strcmp(argv[1], "cond") == 0) {
        test_pthread_cond_run();
    } else if (strcmp(argv[1], "barrier") == 0) {
        test_pthread_barrier_run();
    } else if (strcmp(argv[1], "sem") == 0) {
        test_semaphore_run();
    } else if (strcmp(argv[1], "mqueue") == 0) {
        test_mqueue_run();
    } else if (strcmp(argv[1], "clock") == 0) {
        test_clock_timer_run();
    } else if (strcmp(argv[1], "sched") == 0) {
        test_sched_unistd_run();
    } else {
        printf("Unknown test group: %s\r\n", argv[1]);
        return;
    }

    print_test_summary();
}
SHELL_CMD_EXPORT_ALIAS(cmd_posix_test_single, posix_test_single, Run single POSIX test group);

int main(void)
{
    board_init();

    printf("\r\n========================================\r\n");
    printf("  FreeRTOS POSIX Test Application\r\n");
    printf("========================================\r\n");
    printf("\r\nShell commands:\r\n");
    printf("  posix_test         - Run all tests\r\n");
    printf("  posix_test_single  - Run single test group\r\n");
    printf("\r\n");

    /* Initialize shell task */
    struct bflb_device_s *uart0 = bflb_device_get_by_name("uart0");
    shell_init_with_task(uart0);

    vTaskStartScheduler();   /* starts scheduler, never returns */

    while (1) {
        /* Should never reach here */
    }
}

FAQ

Build fails with \"pthread.h / semaphore.h not found\".

The POSIX compatibility layer is not enabled. Add CONFIG_POSIX=y to defconfig (the example already has it) or enable it in make menuconfig, then rebuild.

POSIX APIs or native FreeRTOS APIs?

No absolute winner: POSIX APIs are more portable (code can move to Linux or other RTOSes); FreeRTOS native APIs are closer to the hardware and better documented. Courses often ask for "standard interfaces"; real projects often mix both.

`posix_test` reports FAILED cases. What now?

First verify your board and defconfig match the example (PSRAM/heap size affects thread creation). If a few cases fail, the stack or heap is usually too small — increase configTOTAL_HEAP_SIZE or thread stacksize and retry.

How long do the tests take?

All 9 groups take a few seconds to tens of seconds, depending on clock/timer waits. Use posix_test_single <group> when debugging one interface.

Released under the MIT License. Build Time 2026-09-11 14:52:23