Skip to content

Commit b081fd1

Browse files
committed
Integrate FreeRTOS
1 parent d50aec5 commit b081fd1

28 files changed

Lines changed: 742 additions & 82 deletions

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "ch32v305/lib/NMSIS"]
88
path = ch32v305/lib/NMSIS
99
url = https://github.com/Nuclei-Software/NMSIS.git
10+
[submodule "ch32v305/lib/FreeRTOS-Kernel"]
11+
path = ch32v305/lib/FreeRTOS-Kernel
12+
url = https://github.com/FreeRTOS/FreeRTOS-Kernel.git

ch32v305/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ set(DISABLEFLOAT16 ON CACHE BOOL "Disable NMSIS float16 kernels" FORCE)
3838
# Work around a lowercase ${project} reference in NMSIS DSP's CMakeLists.txt.
3939
set(project NMSISDSP)
4040
add_subdirectory(lib/NMSIS/NMSIS/DSP/Source)
41+
target_compile_options(NMSISDSP PRIVATE
42+
-fstack-usage
43+
-Wstack-usage=512
44+
)
4145

4246
file(GLOB SRC_FILES CONFIGURE_DEPENDS
4347
lib/Peripheral/src/*.c
@@ -55,18 +59,33 @@ set(FATFS_SOURCES
5559
lib/fatfs/source/ffunicode.c
5660
)
5761

62+
set(FREERTOS_KERNEL_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/lib/FreeRTOS-Kernel)
63+
set(FREERTOS_SOURCES
64+
${FREERTOS_KERNEL_ROOT}/tasks.c
65+
${FREERTOS_KERNEL_ROOT}/queue.c
66+
${FREERTOS_KERNEL_ROOT}/list.c
67+
${FREERTOS_KERNEL_ROOT}/portable/GCC/RISC-V/port.c
68+
${FREERTOS_KERNEL_ROOT}/portable/GCC/RISC-V/portASM.S
69+
src/freertos/port_ch32v30x.S
70+
)
71+
5872
add_executable(${EXECUTABLE}
5973
lib/Startup/startup_ch32v30x_D8C.S
6074
lib/Core/core_riscv.c
6175

6276
${SRC_FILES}
6377
${SRC_FILES_RECURSE}
6478
${FATFS_SOURCES}
79+
${FREERTOS_SOURCES}
6580

6681
)
6782

6883
target_include_directories(${EXECUTABLE} PRIVATE
6984
src
85+
src/freertos
86+
${FREERTOS_KERNEL_ROOT}/include
87+
${FREERTOS_KERNEL_ROOT}/portable/GCC/RISC-V
88+
${FREERTOS_KERNEL_ROOT}/portable/GCC/RISC-V/chip_specific_extensions/RISCV_no_extensions
7089
lib/Core/
7190
lib/Debug/
7291
lib/Peripheral/inc
@@ -81,6 +100,8 @@ target_compile_definitions(${EXECUTABLE} PRIVATE
81100
-DHSE_VALUE=24000000
82101
-DCFG_TUD_WCH_USBIP_USBHS=1
83102
-DCFG_TUSB_MCU=OPT_MCU_CH32V307
103+
configENABLE_FPU=1
104+
configENABLE_VPU=0
84105
)
85106

86107
target_compile_options(${EXECUTABLE} PRIVATE

ch32v305/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,30 @@ wchisp flash ch32v305_sdr.elf
4444

4545
- Link-time output includes `--print-memory-usage`.
4646
- A post-build `riscv-none-elf-size` report is also printed.
47+
- FreeRTOS Kernel is a submodule pinned to V11.3.0. Initialize submodules before
48+
building a fresh checkout:
49+
50+
```bash
51+
git submodule update --init --recursive
52+
```
53+
- All FreeRTOS objects and tasks use static allocation. The task layout is:
54+
55+
| Task | Priority | Stack |
56+
| --- | ---: | ---: |
57+
| I2S processing | 3 | 256 words / 1024 bytes |
58+
| TinyUSB device | 2 | 384 words / 1536 bytes |
59+
| Application/UI | 1 | 512 words / 2048 bytes |
60+
| FreeRTOS idle | 0 | 192 words / 768 bytes |
61+
62+
- The firmware and NMSIS DSP library are built with `-fstack-usage`. With
63+
release LTO, the emitted linked call paths measured 320 bytes for I2S, 576
64+
bytes for USB, 1040 bytes for the application, and 128 bytes for idle. The
65+
allocations include the largest
66+
compiler-reported nested interrupt path, the 256-byte RISC-V integer/FPU
67+
switch frame, and extra margin for prebuilt newlib calls not represented in
68+
this target's `.su` records.
69+
- The kernel tick is 1 kHz, derived from a free-running 144 MHz CH32 SysTick.
70+
Tickless idle installs an absolute compare but intentionally busy-waits
71+
instead of executing `WFI`. The wait tests `CNT >= deadline` as well as
72+
enabled PFIC pending interrupts, so a missed equality compare cannot stall
73+
until the 64-bit counter wraps.

ch32v305/lib/FreeRTOS-Kernel

Submodule FreeRTOS-Kernel added at 9b777ae

ch32v305/lib/Ld/Link.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ SECTIONS
168168
PROVIDE( _heap_end = ORIGIN(RAM) + LENGTH(RAM) - __stack_size );
169169
PROVIDE( _susrstack = _heap_end );
170170
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM) );
171+
PROVIDE( __freertos_irq_stack_top = _eusrstack );
171172

172173
ASSERT( _end <= _heap_end, "RAM overflow: .data/.bss/heap overlap stack" )
173174

174175
}
175-

ch32v305/lib/Startup/startup_ch32v30x_D8C.S

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,13 @@ handle_reset:
358358
/* Enable interrupt nesting and hardware stack */
359359
li t0, 0x0b
360360
csrw 0x804, t0
361-
/* Enable floating point and global interrupt, configure privileged mode */
362-
li t0, 0x6088
363-
csrw mstatus, t0
361+
/*
362+
* FreeRTOS manipulates machine CSRs while creating tasks and in critical
363+
* sections, so enter main in M-mode with interrupts disabled. This matches
364+
* WCH's CH32V30x FreeRTOS startup: FS=dirty, MPP=M, MPIE=0 and MIE=0.
365+
*/
366+
li t0, 0x7800
367+
csrw mstatus, t0
364368
/* Configure the interrupt vector table recognition mode and entry address mode */
365369
la t0, _vector_base
366370
ori t0, t0, 3
@@ -370,5 +374,3 @@ handle_reset:
370374
la t0, main
371375
csrw mepc, t0
372376
mret
373-
374-

ch32v305/src/FreeRTOSConfig.h

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#ifndef FREERTOS_CONFIG_H
2+
#define FREERTOS_CONFIG_H
3+
4+
#include <stdint.h>
5+
6+
#define configCPU_CLOCK_HZ 144000000UL
7+
#define configTICK_RATE_HZ 1000U
8+
#define configUSE_PREEMPTION 1
9+
#define configUSE_TIME_SLICING 0
10+
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
11+
#define configUSE_TICKLESS_IDLE 1
12+
#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2
13+
14+
#define configMAX_PRIORITIES 4
15+
#define configMINIMAL_STACK_SIZE 192U
16+
#define configMAX_TASK_NAME_LEN 16
17+
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_32_BITS
18+
#define configIDLE_SHOULD_YIELD 0
19+
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1
20+
#define configQUEUE_REGISTRY_SIZE 0
21+
#define configENABLE_BACKWARD_COMPATIBILITY 0
22+
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0
23+
#define configUSE_MINI_LIST_ITEM 0
24+
#define configSTACK_DEPTH_TYPE uint32_t
25+
#define configMESSAGE_BUFFER_LENGTH_TYPE uint32_t
26+
#define configUSE_NEWLIB_REENTRANT 0
27+
28+
#define configUSE_TIMERS 0
29+
#define configUSE_EVENT_GROUPS 0
30+
#define configUSE_STREAM_BUFFERS 0
31+
#define configUSE_CO_ROUTINES 0
32+
33+
#define configUSE_TASK_NOTIFICATIONS 1
34+
#define configUSE_MUTEXES 1
35+
#define configUSE_RECURSIVE_MUTEXES 0
36+
#define configUSE_COUNTING_SEMAPHORES 0
37+
#define configUSE_QUEUE_SETS 0
38+
39+
#define configSUPPORT_STATIC_ALLOCATION 1
40+
#define configSUPPORT_DYNAMIC_ALLOCATION 0
41+
42+
#define configUSE_IDLE_HOOK 0
43+
#define configUSE_TICK_HOOK 0
44+
#define configCHECK_FOR_STACK_OVERFLOW 2
45+
#define configUSE_MALLOC_FAILED_HOOK 0
46+
47+
#define configUSE_TRACE_FACILITY 0
48+
#define configGENERATE_RUN_TIME_STATS 0
49+
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
50+
#define configUSE_POSIX_ERRNO 0
51+
52+
#ifndef configENABLE_FPU
53+
#define configENABLE_FPU 1
54+
#endif
55+
#ifndef configENABLE_VPU
56+
#define configENABLE_VPU 0
57+
#endif
58+
59+
/* CH32V30x uses its vendor SysTick/PFIC block, not a standard CLINT. */
60+
#define configMTIME_BASE_ADDRESS 0
61+
#define configMTIMECMP_BASE_ADDRESS 0
62+
63+
#define INCLUDE_vTaskPrioritySet 0
64+
#define INCLUDE_uxTaskPriorityGet 0
65+
#define INCLUDE_vTaskDelete 0
66+
#define INCLUDE_vTaskSuspend 1
67+
#define INCLUDE_vTaskDelayUntil 0
68+
#define INCLUDE_vTaskDelay 1
69+
#define INCLUDE_xTaskAbortDelay 0
70+
#define INCLUDE_xTaskGetHandle 0
71+
#define INCLUDE_xTaskGetCurrentTaskHandle 1
72+
#define INCLUDE_xTaskGetSchedulerState 0
73+
#define INCLUDE_uxTaskGetStackHighWaterMark 1
74+
#define INCLUDE_uxTaskGetStackHighWaterMark2 1
75+
#define INCLUDE_eTaskGetState 0
76+
#define INCLUDE_xTimerPendFunctionCall 0
77+
78+
#ifdef __cplusplus
79+
extern "C" {
80+
#endif
81+
void vFreeRTOSAssert(const char *file, int line);
82+
#ifdef __cplusplus
83+
}
84+
#endif
85+
86+
#define configASSERT(condition) \
87+
do \
88+
{ \
89+
if(!(condition)) \
90+
{ \
91+
vFreeRTOSAssert(__FILE__, __LINE__); \
92+
} \
93+
} while(0)
94+
95+
#endif

ch32v305/src/audio.c

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,11 @@ static volatile uint32_t s_usb_drop_frame_count = 0U;
2626
return s_streaming_alt != 0U;
2727
}
2828

29-
void audio_usb_mic_write_isr(volatile uint16_t const* src_words, size_t word_count)
29+
void audio_usb_mic_write(volatile uint16_t const* src_words, size_t word_count)
3030
{
3131
size_t const frame_count = word_count / 4U;
32-
size_t const sample_count = frame_count * 2U;
3332
size_t const total_bytes = frame_count * AUDIO_USB_FRAME_BYTES;
3433
tu_fifo_t* fifo;
35-
tu_fifo_buffer_info_t info;
3634

3735
if ((!audio_usb_tx_ready()) || (src_words == 0) || (frame_count == 0U)) {
3836
return;
@@ -44,31 +42,18 @@ void audio_usb_mic_write_isr(volatile uint16_t const* src_words, size_t word_cou
4442
}
4543

4644
fifo = tud_audio_get_ep_in_ff();
47-
tu_fifo_get_write_info(fifo, &info);
48-
if ((info.linear.len + info.wrapped.len) < total_bytes) {
45+
if (tu_fifo_remaining(fifo) < total_bytes) {
4946
s_usb_drop_frame_count += (uint32_t)frame_count;
5047
return;
5148
}
5249

53-
/* The I2S producer has already normalized each 32-bit sample in place.
54-
* The FIFO byte buffer is uint16_t-aligned by tinyusb (TUD_EPBUF_DEF
55-
* uses 4-byte alignment), so the linear segment always splits on a
56-
* 4-byte boundary in this 8-bytes-per-frame stream. */
57-
uint32_t const* src32 = (uint32_t const*)(uintptr_t)src_words;
58-
size_t const linear_samples = info.linear.len / sizeof(uint32_t);
59-
size_t const head = (linear_samples < sample_count) ? linear_samples : sample_count;
60-
uint32_t* dst = (uint32_t*)(uintptr_t)info.linear.ptr;
61-
for (size_t i = 0U; i < head; ++i) {
62-
dst[i] = src32[i];
63-
}
64-
if (head < sample_count) {
65-
uint32_t* wrap = (uint32_t*)(uintptr_t)info.wrapped.ptr;
66-
for (size_t i = head; i < sample_count; ++i) {
67-
wrap[i - head] = src32[i];
68-
}
50+
/* The capacity check preserves the all-or-drop DMA-chunk policy. The USB
51+
* consumer can only increase the available space before this write. */
52+
uint16_t const written =
53+
tud_audio_write((void const*)(uintptr_t)src_words, (uint16_t)total_bytes);
54+
if (written != total_bytes) {
55+
s_usb_drop_frame_count += (uint32_t)frame_count;
6956
}
70-
71-
tu_fifo_advance_write_pointer(fifo, (uint16_t)total_bytes);
7257
}
7358

7459
static bool audio_clock_get_request(uint8_t rhport, tusb_control_request_t const* request) {

ch32v305/src/ch32v30x_it.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
1111
*******************************************************************************/
1212
#include "ch32v30x_it.h"
13+
#include "tusb.h"
1314

1415
void NMI_Handler(void) __attribute__((interrupt));
1516
void HardFault_Handler(void) __attribute__((interrupt));
16-
void SW_Handler(void) __attribute__((interrupt));
1717

1818
/*********************************************************************
1919
* @fn NMI_Handler
@@ -44,14 +44,6 @@ void HardFault_Handler(void)
4444
}
4545
}
4646

47-
#include "tusb.h"
48-
49-
void SW_Handler(void) {
50-
NVIC_ClearPendingIRQ(Software_IRQn);
51-
tud_task();
52-
}
53-
5447
__attribute__((interrupt)) void USBHS_IRQHandler(void) {
5548
tud_int_handler(0);
56-
NVIC_SetPendingIRQ(Software_IRQn);
5749
}

ch32v305/src/demod/am.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ void am_process_i2s_words(const uint16_t *words, size_t frame_count, uint32_t ga
103103
int32_t audio = s_audio_cic.push(normalized_audio);
104104

105105
uint16_t const dac12 = demod::audio_to_dac12(audio, gain_q16, kDacShift);
106-
dac_hw_stream_fm_push_sample_isr(dac12);
106+
dac_hw_stream_fm_push_sample(dac12);
107107
}
108108

109109
s_norm_gain_q30 = carrier_norm_gain_q30(carrier);

0 commit comments

Comments
 (0)