丁語婕, 鄭煦霖, 王信智
FreeRTOS is a lightweight real-time operating system designed for embedded systems, offering efficient scheduling and scalability. Its robust kernel and modular design make it ideal for responsive and reliable applications across various hardware and industries.
SMP enables multiple processors to share memory and execute tasks in parallel, enhancing performance for applications. In FreeRTOS, a single instance operates across cores with shared memory and the same architecture, allowing multiple tasks to run simultaneously, which challenges traditional priority-based task execution.
This project focuses on implementing FreeRTOS SMP for the RISC-V architecture, exploring two key aspects: FreeRTOS with RISC-V, which involves adapting FreeRTOS to the RISC-V environment by modifying its port layer for hardware compatibility, and SMP with FreeRTOS, which extends the system to support multiprocessor execution by updating task scheduling, resource management, and synchronization mechanisms.
~/.local/xPacks/@xpack-dev-tools/riscv-none-elf-gcc/14.2.0-2.1/.content/bin
Parameters
Parameters | Description |
---|---|
-nographic |
Disable graphical output and redirect serial I/Os to console |
-machine virt |
Select emulated machine, virt is RISC-V VirtIO board |
-net none |
Disable all network functionality in the virtual machine |
-chardev stdio,id=con,mux=on |
A character device connects to the host's standard input(stdin) and standard output(stdout). id=id assigns a unique identifier to the device. mux=on enables multiplexing, allowing multiple virtual devices to share the same terminal connection |
-serial chardev:con |
Redirect the serial port to character device named con , enabling communication through it |
-mon chardev=con,mode=realine |
Set up a QEMU monitor interface connected to the character device con and enable readline mode for interactive command-line input |
-bios none |
Not to load a BIOS for the virtual machine |
-smp 4 |
Set the number of initial CPUs to 4 |
-kernel ./build/gcc/output/RTOSDemo.elf |
Use ./build/gcc/output/RTOSDemo.elf as kernel image |
Output
-s
: Starts a GDB server on default TCP port 1234 for debugging.-S
: Starts QEMU in a paused state, waiting for a GDB to connect and resume execution../build/gcc/output/RTOSDemo.elf
: Specify the ELF file to debug, containing symbols and code for debugging.-ex "target remote :1234"
: Execute the GDB command target remote :1234 to connect to the QEMU GDB server running on TCP port 1234.Parameters | Description |
---|---|
configMTIME_BASE_ADDRESS |
set configMTIME_BASE_ADDRESS to the MTIME base address |
configMTIMECMP_BASE_ADDRESS |
set configMTIMECMP_BASE_ADDRESS to the MTIMECMP base address |
To achieve the transition to a dedicated interrupt stack before any C functions are called from an interrupt service routine (ISR) in the FreeRTOS RISC-V port, two methods are available: using a linker script or a static allocated array.We chose the latter method for its simplicity and efficiency, declaring a 300-word stack. The stack size is defined in FreeRTOSConfig.h
, ensuring consistent memory usage and avoiding complex linker script modifications.
freertos_risc_v_trap_handler()
is the FreeRTOS trap handler and serves as the central entry point for all interrupts and exceptions. To automatically install the trap handler, set portasmHAS_MTIME
to 1.
Parameters | Description |
---|---|
portasmHAS_SIFIVE_CLINT |
target RISC-V chip includes a CLINT and trap handler to be installed automatically then set to 1 |
portasmADDITIONAL_CONTEXT_SIZE |
the number of additional registers that exist on the target chip; temporarily assume it to be 0 |
This Phase is responsible for converting the source files(.c and .S) into a final executable binary, RTOSDemo.elf, that meets RISC-V architecture requirements. The process begins with preparing the necessary tools and flags for the build. Source files are compiled into object files, and dependencies are tracked to ensure only modified files are recompiled. Afterward, the object files are linked together to generate the final executable. The process is managed through a Makefile that defines the necessary steps for compilation and linking, including memory layout and stack size configurations. The result elf file is a binary ready for execution on a RISC-V system.
Finished!
Shrink the code listing. You should only mention the critical parts.
start.S
port.c
Comment section /* ----- [SMP-Specify] ----- */
represents modifications that satisfy SMP requirements.
start.S
, the primary hart performs system initialization, including setting up .data
and .bss
sections and calling the main function. It signals its readiness to secondary harts using a shared hart_ready_flag
. Secondary harts poll this flag and only proceed to their designated entry point (xPortStartScheduler
) after the primary hart completes initialization. Additionally, stack memory for secondary harts is allocated and aligned to support their execution.xPortStartScheduler()
, the primary hart finalizes its role in initializing the system by setting the hart_ready_flag
and ensuring memory synchronization, allowing secondary harts to proceed. Once all harts are synchronized, the scheduler sets up the interrupt mechanism, including the timer and external interrupts, and transitions to task execution by calling xPortStartFirstTask()
.hart_ready_flag
to verify that the primary hart initializes before starting the secondary harts
hart_ready_flag
to 1. Once the hart_ready_flag
is set to 1, the secondary harts will enter the entry point xxPortStartScheduler
in port.c
.
Mention what you have found by means of GDB tracing.
In the FreeRTOSConfig.h
, set the number of cores with #define configNUMBER_OF_CORES 4
(assuming a core number greater than 2), and use the error output to modify the program to enable SMP.
portGET_CORE_ID()
portYIELD_CORE()
portSET_INTERRUPT_MASK()
& portCLEAR_INTERRUPT_MASK()
portRELEASE_TASK_LOCK
& portGET_TASK_LOCK
& portRELEASE_ISR_LOCK
& portGET_ISR_LOCK
portENTER_CRITICAL_FROM_ISR
& portEXIT_CRITICAL_FROM_ISR
CLINT is a simple core local interrupt controller designed for embedded systems to provide timer interrupts, software interrupts, and local interrupt management. It is used for handling asynchronous CPU events such as timers and software-triggered interrupts.
mtime
and mtimecmp
registers.mtime
reaches the value in mtimecmp
.msip
, enabling inter-core communication by notifying a CPU core to execute specific operations.mtime
: Provides a monotonically increasing time value for precise timing.mtimecmp
: Configures the threshold for triggering timer interrupts.msip
: Used to trigger software interrupts for inter-core signaling.ACLINT is an enhanced version of CLINT, designed for multi-core systems and modular architectures. It provides machine-level and supervisor-level timer and software interrupt functionalities. Its modular design allows selective integration of features like inter-processor interrupts (IPI) and time synchronization.
mtime
, mtimecmp
, and msip
.mtime
registers.In
FreeRTOSConfig.h
, adding#define configUSE_CORE_AFFINITY = 1
to enable the function to set which core the task shall run on.
vTaskCoreAffinitySet
UBaseType_t uxCoreAffinityMask
to record which cores a task can run on using a bitwise value.0101
indicates that the task can run on core 0 and core 2.vTaskCoreAffinityGet
Type | Create Task | Create Affinity Set |
---|---|---|
Dynamic | xTaskCreate |
xTaskCreateAffinitySet |
Static | xTaskCreateStatic |
xTaskCreateStaticAffinitySet |
Restricted | xTaskCreateRestricted |
xTaskCreateRestrictedAffinitySet |
Restricted Static | xTaskCreateRestrictedStatic |
xTaskCreateRestrictedStaticAffinitySet |
uxCoreAffinityMask
to allow the task to run on the default core(s) defined by configTASK_DEFAULT_CORE_AFFINITY
(usually all cores).prvYieldCore(xCoreID);
.vTaskStartScheduler
UBaseType_t
can represent all cores.prvYieldForTask
uxCoreAffinityMask
of the task to ensure it can only run on the allowed core(s) (xCore
).prvSelectHighestPriorityTask
pxPreviousTCB
, which points to the TCB of the task that was previously executed on that core, used to preserve the state before the scheduling occurs.uxCoreAffinityMask
of the task to ensure it can only run on the allowed core(s) (xCore
).