diff --git a/.github/scripts/ci_set_matrix.py b/.github/scripts/ci_set_matrix.py index 6b7866a745..4a50185d69 100755 --- a/.github/scripts/ci_set_matrix.py +++ b/.github/scripts/ci_set_matrix.py @@ -37,6 +37,7 @@ "ch32v10x": ["riscv-gcc"], "ch32v20x": ["riscv-gcc"], "ch32v30x": ["riscv-gcc"], + "ch32x035": ["riscv-gcc"], "ch583": ["riscv-gcc"], "da1469x": ["arm-gcc"], "f1c100s": ["arm-gcc"], diff --git a/README.rst b/README.rst index 7fa8d70f10..e1abb7c9ad 100644 --- a/README.rst +++ b/README.rst @@ -314,6 +314,8 @@ Supported CPUs | | CH32V305, CH32V307 | 🟡 | | ✅ | ch32_usbfs/hs | ISO data loss [2]_ | | +-----------------------------+--------+------+-----------+------------------------+--------------------+ | | CH583/2, CH32V103 | 🟡 | | ❌ | ch32_usbfs | ISO data loss [2]_ | +| +-----------------------------+--------+------+-----------+------------------------+--------------------+ +| | CH32X033, CH32X035 | 🟡 | | ❌ | ch32_usbfs | ISO data loss [2]_ | +--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+ Table Legend diff --git a/docs/reference/boards.rst b/docs/reference/boards.rst index 1334f6e698..a1d1d6d67a 100644 --- a/docs/reference/boards.rst +++ b/docs/reference/boards.rst @@ -373,17 +373,18 @@ fomu fomu fomu https://tomu.im/fomu.html WCH --- -================== ============================= ======== ===================================================================== ====== -Board Name Family URL Note -================== ============================= ======== ===================================================================== ====== -ch32f205r-r0 CH32F205r-r0 ch32f20x https://github.com/openwch/ch32f20x -ch32v103c_bluepill CH32V103C8T6-Bluepill ch32v10x https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill -ch32v103r_r1_1v0 CH32V103R-R1-1v1 ch32v10x https://github.com/openwch/ch32v103/tree/main/SCHPCB/CH32V103R-R1-1v1 -ch32v203c_r0_1v0 CH32V203C-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0 -ch32v203g_r0_1v0 CH32V203G-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0 -nanoch32v203 nanoCH32V203 ch32v20x https://github.com/wuxx/nanoCH32V203 -ch32v307v_r1_1v0 CH32V307V-R1-1v0 ch32v30x https://github.com/openwch/ch32v307/tree/main/SCHPCB/CH32V307V-R1-1v0 -nanoch32v305 nanoCH32V305 ch32v30x https://github.com/wuxx/nanoCH32V305 -ch582m_evt CH582M-EVT evaluation board ch583 https://www.wch-ic.com/products/CH582.html -yd-ch582m yd-ch582m from vcc-gnd studio ch583 http://vcc-gnd.com/ -================== ============================= ======== ===================================================================== ====== +========================= ============================= ======== ===================================================================== ====== +Board Name Family URL Note +========================= ============================= ======== ===================================================================== ====== +ch32f205r-r0 CH32F205r-r0 ch32f20x https://github.com/openwch/ch32f20x +ch32v103c_bluepill CH32V103C8T6-Bluepill ch32v10x https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill +ch32v103r_r1_1v0 CH32V103R-R1-1v1 ch32v10x https://github.com/openwch/ch32v103/tree/main/SCHPCB/CH32V103R-R1-1v1 +ch32v203c_r0_1v0 CH32V203C-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0 +ch32v203g_r0_1v0 CH32V203G-R0-1v0 ch32v20x https://github.com/openwch/ch32v20x/tree/main/SCHPCB/CH32V203C-R0 +nanoch32v203 nanoCH32V203 ch32v20x https://github.com/wuxx/nanoCH32V203 +ch32v307v_r1_1v0 CH32V307V-R1-1v0 ch32v30x https://github.com/openwch/ch32v307/tree/main/SCHPCB/CH32V307V-R1-1v0 +nanoch32v305 nanoCH32V305 ch32v30x https://github.com/wuxx/nanoCH32V305 +weact_ch32x035_core_board WeAct CH32X035 Core Board ch32x035 https://github.com/WeActStudio/WeActStudio.CH32X035CoreBoard +ch582m_evt CH582M-EVT evaluation board ch583 https://www.wch-ic.com/products/CH582.html +yd-ch582m yd-ch582m from vcc-gnd studio ch583 http://vcc-gnd.com/ +========================= ============================= ======== ===================================================================== ====== diff --git a/docs/reference/dependencies.rst b/docs/reference/dependencies.rst index b4685f4c08..204b067dba 100644 --- a/docs/reference/dependencies.rst +++ b/docs/reference/dependencies.rst @@ -93,6 +93,7 @@ hw/mcu/wch/ch32f20x https://github.com/openwch/ch32f20x.gi hw/mcu/wch/ch32v103 https://github.com/openwch/ch32v103.git 7578cae0b21f86dd053a1f781b2fc6ab99d0ec17 ch32v10x hw/mcu/wch/ch32v20x https://github.com/openwch/ch32v20x.git c4c38f507e258a4e69b059ccc2dc27dde33cea1b ch32v20x hw/mcu/wch/ch32v307 https://github.com/openwch/ch32v307.git 184f21b852cb95eed58e86e901837bc9fff68775 ch32v30x +hw/mcu/wch/ch32x035 https://github.com/openwch/ch32x035.git c6253942b29ff2a3f5e2829182a874afffa45881 ch32x035 hw/mcu/wch/ch583 https://github.com/openwch/ch583.git bd508ad7ceed48377619837051412a651952857f ch583 lib/CMSIS_5 https://github.com/ARM-software/CMSIS_5.git 2b7495b8535bdcb306dac29b9ded4cfb679d7e5c kinetis_k kinetis_kl lpc54 rw61x mm32 msp432e4 nrf samd2x_l2x lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43 stm32c0 stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5 stm32h7 stm32h7rs stm32l0 stm32l1 stm32l4 stm32l5 stm32u0 stm32u5 stm32wb stm32wba sam3x samd11 samd21 samd2x_l2x samd51 samd5x_e5x same5x same7x samg tm4c lib/CMSIS_6 https://github.com/ARM-software/CMSIS_6.git 6f0a58d01aa9bd2feba212097f9afe7acd991d52 imxrt kinetis_k32l ra stm32n6 lpc51 lpc55 mcx stm32c5 diff --git a/examples/device/audio_4_channel_mic/skip.txt b/examples/device/audio_4_channel_mic/skip.txt index e5e74cd60a..53f8f9c5a2 100644 --- a/examples/device/audio_4_channel_mic/skip.txt +++ b/examples/device/audio_4_channel_mic/skip.txt @@ -3,3 +3,4 @@ mcu:SAMG family:broadcom_64bit family:espressif mcu:CH583 +mcu:CH32X035 diff --git a/examples/device/audio_4_channel_mic_freertos/skip.txt b/examples/device/audio_4_channel_mic_freertos/skip.txt index d47f1d327c..4b3fefb1fc 100644 --- a/examples/device/audio_4_channel_mic_freertos/skip.txt +++ b/examples/device/audio_4_channel_mic_freertos/skip.txt @@ -3,6 +3,7 @@ mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CH583 +mcu:CH32X035 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/audio_test/skip.txt b/examples/device/audio_test/skip.txt index 862c91c6f3..b48f79a679 100644 --- a/examples/device/audio_test/skip.txt +++ b/examples/device/audio_test/skip.txt @@ -2,3 +2,4 @@ mcu:SAMD11 mcu:SAMG family:espressif mcu:CH583 +mcu:CH32X035 diff --git a/examples/device/audio_test_freertos/skip.txt b/examples/device/audio_test_freertos/skip.txt index 70aa232d07..262833b619 100644 --- a/examples/device/audio_test_freertos/skip.txt +++ b/examples/device/audio_test_freertos/skip.txt @@ -3,6 +3,7 @@ mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CH583 +mcu:CH32X035 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/audio_test_multi_rate/skip.txt b/examples/device/audio_test_multi_rate/skip.txt index 862c91c6f3..b48f79a679 100644 --- a/examples/device/audio_test_multi_rate/skip.txt +++ b/examples/device/audio_test_multi_rate/skip.txt @@ -2,3 +2,4 @@ mcu:SAMD11 mcu:SAMG family:espressif mcu:CH583 +mcu:CH32X035 diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt index 24e6931419..3bc1c4dd2e 100644 --- a/examples/device/cdc_msc_freertos/skip.txt +++ b/examples/device/cdc_msc_freertos/skip.txt @@ -3,6 +3,7 @@ mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CH583 +mcu:CH32X035 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt index 049ea996f1..5e10774144 100644 --- a/examples/device/hid_composite_freertos/skip.txt +++ b/examples/device/hid_composite_freertos/skip.txt @@ -3,6 +3,7 @@ mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CH583 +mcu:CH32X035 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/midi_test_freertos/skip.txt b/examples/device/midi_test_freertos/skip.txt index 049ea996f1..5e10774144 100644 --- a/examples/device/midi_test_freertos/skip.txt +++ b/examples/device/midi_test_freertos/skip.txt @@ -3,6 +3,7 @@ mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 mcu:CH583 +mcu:CH32X035 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt index 53836b581c..71bf26fdbd 100644 --- a/examples/device/net_lwip_webserver/skip.txt +++ b/examples/device/net_lwip_webserver/skip.txt @@ -1,6 +1,7 @@ mcu:APM32F0XX mcu:CH32V103 mcu:CH32V20X +mcu:CH32X035 mcu:LPC11UXX mcu:LPC13XX mcu:LPC15XX diff --git a/examples/device/uac2_headset/skip.txt b/examples/device/uac2_headset/skip.txt index 3159cb1768..d53fb1d843 100644 --- a/examples/device/uac2_headset/skip.txt +++ b/examples/device/uac2_headset/skip.txt @@ -6,3 +6,6 @@ mcu:SAMG board:stm32l052dap52 family:espressif mcu:CH583 +mcu:CH32X035 +mcu:CH32V103 +mcu:CH32V20X diff --git a/examples/device/uac2_speaker_fb/skip.txt b/examples/device/uac2_speaker_fb/skip.txt index 88df3e549b..17f9c6dbe2 100644 --- a/examples/device/uac2_speaker_fb/skip.txt +++ b/examples/device/uac2_speaker_fb/skip.txt @@ -6,3 +6,6 @@ mcu:SAMG board:stm32l052dap52 family:broadcom_64bit mcu:CH583 +mcu:CH32X035 +mcu:CH32V103 +mcu:CH32V20X diff --git a/examples/device/uac2_speaker_fb/src/audio_debug.py b/examples/device/uac2_speaker_fb/src/audio_debug.py index 1c6035a440..d345c4a116 100755 --- a/examples/device/uac2_speaker_fb/src/audio_debug.py +++ b/examples/device/uac2_speaker_fb/src/audio_debug.py @@ -14,7 +14,7 @@ # Example must be compiled with CFG_AUDIO_DEBUG=1 VID = 0xcafe -PID = 0x4014 +PID = 0x401b CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX = 2 diff --git a/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.cmake b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.cmake new file mode 100644 index 0000000000..4687e127fe --- /dev/null +++ b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.cmake @@ -0,0 +1,8 @@ +set(LD_FLASH_SIZE 62K) +set(LD_RAM_SIZE 20K) + +function(update_board TARGET) + target_compile_definitions(${TARGET} PUBLIC + SYSCLK_FREQ_48MHz_HSI=48000000 + ) +endfunction() diff --git a/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.h b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.h new file mode 100644 index 0000000000..e42970ed70 --- /dev/null +++ b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.h @@ -0,0 +1,23 @@ +/* metadata: + name: WeAct CH32X035 Core Board + url: https://github.com/WeActStudio/WeActStudio.CH32X035CoreBoard +*/ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define LED_PORT GPIOB +#define LED_PIN GPIO_Pin_12 +#define LED_STATE_ON 1 +#define LED_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE) +#define LED_MODE GPIO_Mode_Out_PP + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.mk b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.mk new file mode 100644 index 0000000000..b6698c9721 --- /dev/null +++ b/hw/bsp/ch32x035/boards/weact_ch32x035_core_board/board.mk @@ -0,0 +1,5 @@ +CFLAGS += -DSYSCLK_FREQ_48MHz_HSI=48000000 + +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=62K \ + -Wl,--defsym=__RAM_SIZE=20K \ diff --git a/hw/bsp/ch32x035/ch32x035_conf.h b/hw/bsp/ch32x035/ch32x035_conf.h new file mode 100644 index 0000000000..9672f983ff --- /dev/null +++ b/hw/bsp/ch32x035/ch32x035_conf.h @@ -0,0 +1,23 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : ch32x035_conf.h + * Author : WCH + * Version : V1.0.0 + * Date : 2023/04/06 + * Description : Library configuration file. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __CH32X035_CONF_H +#define __CH32X035_CONF_H + +#include "ch32x035_dbgmcu.h" +#include "ch32x035_flash.h" +#include "ch32x035_gpio.h" +#include "ch32x035_misc.h" +#include "ch32x035_rcc.h" +#include "ch32x035_usb.h" +#include "ch32x035_usart.h" + +#endif diff --git a/hw/bsp/ch32x035/ch32x035_it.h b/hw/bsp/ch32x035/ch32x035_it.h new file mode 100644 index 0000000000..5d6c5d9b2a --- /dev/null +++ b/hw/bsp/ch32x035/ch32x035_it.h @@ -0,0 +1,29 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : ch32x035_it.h + * Author : WCH + * Version : V1.0.0 + * Date : 2023/04/06 + * Description : This file contains the headers of the interrupt handlers. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __CH32X035_IT_H +#define __CH32X035_IT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ch32x035.h" + +void USBFS_IRQHandler(void); +void USBFSWakeUp_IRQHandler(void); +void SysTick_Handler(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch32x035/family.c b/hw/bsp/ch32x035/family.c new file mode 100644 index 0000000000..4c84b3c1eb --- /dev/null +++ b/hw/bsp/ch32x035/family.c @@ -0,0 +1,175 @@ +/* metadata: +manufacturer: WCH +*/ + +#include +#include + +// https://github.com/openwch/ch32v307/pull/90 +// https://github.com/openwch/ch32v20x/pull/12 +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + +#include "ch32x035.h" +#include "ch32x035_it.h" + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif + +#include "bsp/board_api.h" +#include "board.h" + +__attribute__((interrupt)) __attribute__((used)) void USBFS_IRQHandler(void) { +#if CFG_TUD_ENABLED && CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); +#endif +} + +__attribute__((interrupt)) __attribute__((used)) void USBFSWakeUp_IRQHandler(void) { +#if CFG_TUD_ENABLED && CFG_TUD_WCH_USBIP_USBFS + tud_int_handler(0); +#endif +} + +//--------------------------------------------------------------------+ +// Board API +//--------------------------------------------------------------------+ +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; + +__attribute__((interrupt)) void SysTick_Handler(void) { + SysTick->SR = 0; + system_ticks++; +} + +static uint32_t SysTick_Config(uint32_t ticks) { + NVIC_EnableIRQ(SysTicK_IRQn); + SysTick->CTLR = 0; + SysTick->SR = 0; + SysTick->CNT = 0; + SysTick->CMP = ticks - 1; + SysTick->CTLR = 0xF; + return 0; +} + +uint32_t tusb_time_millis_api(void) { + return system_ticks; +} +#endif + +void board_init(void) { + __disable_irq(); + + SystemCoreClockUpdate(); + +#if CFG_TUSB_OS == OPT_OS_NONE + SysTick_Config(SystemCoreClock / 1000); +#endif + + LED_CLOCK_EN(); + + GPIO_InitTypeDef GPIO_InitStructure = { + .GPIO_Pin = LED_PIN, + .GPIO_Mode = LED_MODE, + .GPIO_Speed = GPIO_Speed_50MHz, + }; + GPIO_Init(LED_PORT, &GPIO_InitStructure); + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBFS, ENABLE); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_16; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_17; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; + GPIO_Init(GPIOC, &GPIO_InitStructure); + + AFIO->CTLR = (AFIO->CTLR & ~(AFIO_CTLR_UDP_PUE | AFIO_CTLR_UDM_PUE)) | AFIO_CTLR_USB_PHY_V33 | AFIO_CTLR_UDP_PUE | + AFIO_CTLR_USB_IOEN; + +#ifdef UART_DEV + UART_CLOCK_EN(); + GPIO_InitTypeDef usart_init = { + .GPIO_Pin = UART_TX_PIN | UART_RX_PIN, + .GPIO_Speed = GPIO_Speed_50MHz, + .GPIO_Mode = GPIO_Mode_AF_PP, + }; + GPIO_Init(GPIOA, &usart_init); + + USART_InitTypeDef usart = { + .USART_BaudRate = 115200, + .USART_WordLength = USART_WordLength_8b, + .USART_StopBits = USART_StopBits_1, + .USART_Parity = USART_Parity_No, + .USART_Mode = USART_Mode_Tx | USART_Mode_Rx, + .USART_HardwareFlowControl = USART_HardwareFlowControl_None, + }; + USART_Init(UART_DEV, &usart); + USART_Cmd(UART_DEV, ENABLE); +#endif + + __enable_irq(); +} + +void board_reset_to_bootloader(void) { +} + +void board_led_write(bool state) { + GPIO_WriteBit(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON)); +} + +uint32_t board_button_read(void) { + return false; +} + +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + volatile uint32_t *ch32_uuid = ((volatile uint32_t *)0x1FFFF7E8UL); + uint32_t uid[3] = {ch32_uuid[0], ch32_uuid[1], ch32_uuid[2]}; + size_t len = TU_MIN(max_len, sizeof(uid)); + memcpy(id, uid, len); + + return len; +} + +int board_uart_read(uint8_t *buf, int len) { +#ifdef UART_DEV + int count; + for (count = 0; count < len; count++) { + if (USART_GetFlagStatus(UART_DEV, USART_FLAG_RXNE) == RESET) { + break; + } + buf[count] = USART_ReceiveData(UART_DEV); + } + return count; +#else + (void)buf; + (void)len; + return 0; +#endif +} + +int board_uart_write(const void *buf, int len) { +#ifdef UART_DEV + const uint8_t *p = (const uint8_t *)buf; + int count = 0; + while (count < len) { + if (USART_GetFlagStatus(UART_DEV, USART_FLAG_TXE) != RESET) { + USART_SendData(UART_DEV, p[count]); + count++; + } else { + break; + } + } + return count; +#else + (void)buf; + (void)len; + return 0; +#endif +} diff --git a/hw/bsp/ch32x035/family.cmake b/hw/bsp/ch32x035/family.cmake new file mode 100644 index 0000000000..6ccf9022e4 --- /dev/null +++ b/hw/bsp/ch32x035/family.cmake @@ -0,0 +1,103 @@ +include_guard() + +set(UF2_FAMILY_ID 0x699b62ec) +set(CH32_FAMILY ch32x035) +set(SDK_DIR ${TOP}/hw/mcu/wch/${CH32_FAMILY}) +set(SDK_SRC_DIR ${SDK_DIR}/EVT/EXAM/SRC) + +# include board specific +include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake) + +# toolchain set up +set(CMAKE_SYSTEM_CPU rv32imac-ilp32 CACHE INTERNAL "System Processor") +set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake) + +set(FAMILY_MCUS CH32X035 CACHE INTERNAL "") +set(OPENOCD_OPTION "-f ${CMAKE_CURRENT_LIST_DIR}/wch-riscv.cfg") + +#------------------------------------ +# Startup & Linker script +#------------------------------------ +if (NOT DEFINED LD_FILE_GNU) + set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/linker/${CH32_FAMILY}.ld) +endif () +set(LD_FILE_Clang ${LD_FILE_GNU}) +if (NOT DEFINED STARTUP_FILE_GNU) + set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_${CH32_FAMILY}.S) +endif () +set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU}) + +#------------------------------------ +# Board Target +#------------------------------------ +function(family_add_board BOARD_TARGET) + add_library(${BOARD_TARGET} STATIC + ${SDK_SRC_DIR}/Core/core_riscv.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_dbgmcu.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c + ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_usart.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_${CH32_FAMILY}.c + ) + target_include_directories(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/Core + ${SDK_SRC_DIR}/Peripheral/inc + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ) + target_compile_definitions(${BOARD_TARGET} PUBLIC + CFG_EXAMPLE_MSC_DUAL_READONLY + CFG_EXAMPLE_VIDEO_READONLY + ) + + update_board(${BOARD_TARGET}) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + target_compile_options(${BOARD_TARGET} PUBLIC -mcmodel=medany) + endif() +endfunction() + +#------------------------------------ +# Functions +#------------------------------------ +function(family_configure_example TARGET RTOS) + family_configure_common(${TARGET} ${RTOS}) + family_add_tinyusb(${TARGET} OPT_MCU_CH32X035) + + target_sources(${TARGET} PUBLIC + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c + ${TOP}/src/portable/wch/dcd_ch32_usbfs.c + ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} + ) + target_include_directories(${TARGET} PUBLIC + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../ + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD} + ) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_link_options(${TARGET} PUBLIC + -nostartfiles + --specs=nosys.specs --specs=nano.specs + -Wl,--defsym=__FLASH_SIZE=${LD_FLASH_SIZE} + -Wl,--defsym=__RAM_SIZE=${LD_RAM_SIZE} + "LINKER:--script=${LD_FILE_GNU}" + ) + elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") + message(FATAL_ERROR "Clang is not supported") + elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR") + target_link_options(${TARGET} PUBLIC + "LINKER:--config=${LD_FILE_IAR}" + ) + endif () + + set_source_files_properties(${STARTUP_FILE_${CMAKE_C_COMPILER_ID}} PROPERTIES + SKIP_LINTING ON + COMPILE_OPTIONS -w) + + # Flashing + family_add_bin_hex(${TARGET}) + family_flash_openocd(${TARGET}) + family_flash_wlink_rs(${TARGET}) +endfunction() diff --git a/hw/bsp/ch32x035/family.mk b/hw/bsp/ch32x035/family.mk new file mode 100644 index 0000000000..f389bed723 --- /dev/null +++ b/hw/bsp/ch32x035/family.mk @@ -0,0 +1,46 @@ +# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack +CROSS_COMPILE ?= riscv-none-elf- + +CH32_FAMILY = ch32x035 +SDK_DIR = hw/mcu/wch/ch32x035 +SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC + +include $(TOP)/$(BOARD_PATH)/board.mk +CPU_CORE ?= rv32imac-ilp32 + +CFLAGS += \ + -mcmodel=medany \ + -ffat-lto-objects \ + -flto \ + -DCFG_TUSB_MCU=OPT_MCU_CH32X035 \ + -DCFG_EXAMPLE_MSC_DUAL_READONLY=1 \ + -DCFG_EXAMPLE_VIDEO_READONLY=1 + +CFLAGS += -Wno-error=strict-prototypes + +LDFLAGS += \ + -nostdlib -nostartfiles \ + --specs=nosys.specs --specs=nano.specs \ + +LD_FILE = $(FAMILY_PATH)/linker/${CH32_FAMILY}.ld + +SRC_C += \ + src/portable/wch/dcd_ch32_usbfs.c \ + $(SDK_SRC_DIR)/Core/core_riscv.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_dbgmcu.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \ + $(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c \ + +SRC_S += $(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}.S + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(SDK_SRC_DIR)/Core \ + $(TOP)/$(SDK_SRC_DIR)/Peripheral/inc \ + +FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V + +OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg +flash: flash-wlink-rs diff --git a/hw/bsp/ch32x035/linker/ch32x035.ld b/hw/bsp/ch32x035/linker/ch32x035.ld new file mode 100644 index 0000000000..c52b058046 --- /dev/null +++ b/hw/bsp/ch32x035/linker/ch32x035.ld @@ -0,0 +1,151 @@ +/* Define default values if not already defined */ +__flash_size = DEFINED(__FLASH_SIZE) ? __FLASH_SIZE : 62K; +__ram_size = DEFINED(__RAM_SIZE) ? __RAM_SIZE : 20K; +__stack_size = DEFINED(__STACK_SIZE) ? __STACK_SIZE : 2048; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __flash_size + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __ram_size +} + +ENTRY( _start ) + +PROVIDE( _stack_size = __stack_size ); +_estack = ORIGIN(RAM) + LENGTH(RAM); + +SECTIONS +{ + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + .vector : + { + *(.vector); + . = ALIGN(64); + } >FLASH AT>FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.gnu.linkonce.t.*) + . = ALIGN(4); + } >FLASH AT>FLASH + + .fini : + { + KEEP(*(SORT_NONE(.fini))) + . = ALIGN(4); + } >FLASH AT>FLASH + + PROVIDE( _etext = . ); + PROVIDE( _eitcm = . ); + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH AT>FLASH + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH AT>FLASH + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH AT>FLASH + + .ctors : + { + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } >FLASH AT>FLASH + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } >FLASH AT>FLASH + + .dalign : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + } >RAM AT>FLASH + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.sdata2.*) + *(.gnu.linkonce.s.*) + . = ALIGN(8); + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata .srodata.*) + . = ALIGN(4); + PROVIDE( _edata = .); + } >RAM AT>FLASH + + .bss : + { + . = ALIGN(4); + PROVIDE( _sbss = .); + *(.sbss*) + *(.gnu.linkonce.sb.*) + *(.bss*) + *(.gnu.linkonce.b.*) + *(COMMON*) + . = ALIGN(4); + PROVIDE( _ebss = .); + } >RAM AT>FLASH + + PROVIDE( _end = _ebss); + PROVIDE( end = . ); + + .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : + { + PROVIDE( _heap_end = . ); + . = ALIGN(4); + PROVIDE(_susrstack = . ); + . = . + __stack_size; + PROVIDE( _eusrstack = .); + } >RAM +} diff --git a/hw/bsp/ch32x035/system_ch32x035.c b/hw/bsp/ch32x035/system_ch32x035.c new file mode 100644 index 0000000000..1a48906042 --- /dev/null +++ b/hw/bsp/ch32x035/system_ch32x035.c @@ -0,0 +1,76 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : system_ch32x035.c + * Author : WCH + * Version : V1.0.0 + * Date : 2023/04/06 + * Description : CH32X035 Device Peripheral Access Layer System Source File. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#include "ch32x035.h" + +#if defined SYSCLK_FREQ_8MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_8MHz_HSI; +#elif defined SYSCLK_FREQ_12MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_12MHz_HSI; +#elif defined SYSCLK_FREQ_16MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_16MHz_HSI; +#elif defined SYSCLK_FREQ_24MHz_HSI +uint32_t SystemCoreClock = SYSCLK_FREQ_24MHz_HSI; +#else +uint32_t SystemCoreClock = HSI_VALUE; +#endif + +__I uint8_t AHBPrescTable[16] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; + +static void SetSysClock(void); + +void SystemInit(void) { + RCC->CTLR |= (uint32_t)0x00000001; + RCC->CFGR0 |= (uint32_t)0x00000050; + RCC->CFGR0 &= (uint32_t)0xF8FFFF5F; + SetSysClock(); +} + +void SystemCoreClockUpdate(void) { + uint32_t tmp; + + SystemCoreClock = HSI_VALUE; + tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)]; + + if (((RCC->CFGR0 & RCC_HPRE) >> 4) < 8) { + SystemCoreClock /= tmp; + } else { + SystemCoreClock >>= tmp; + } +} + +static void SetSysClock(void) { + GPIO_IPD_Unused(); + + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_2; + + RCC->CFGR0 &= (uint32_t)0xFFFFFF0F; + +#if defined SYSCLK_FREQ_8MHz_HSI + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV6; + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0; +#elif defined SYSCLK_FREQ_12MHz_HSI + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV4; + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0; +#elif defined SYSCLK_FREQ_16MHz_HSI + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV3; + FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY); + FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1; +#elif defined SYSCLK_FREQ_24MHz_HSI + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2; + FLASH->ACTLR = (uint32_t)FLASH_ACTLR_LATENCY_1; +#else + RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1; +#endif +} diff --git a/hw/bsp/ch32x035/system_ch32x035.h b/hw/bsp/ch32x035/system_ch32x035.h new file mode 100644 index 0000000000..173b16c1b9 --- /dev/null +++ b/hw/bsp/ch32x035/system_ch32x035.h @@ -0,0 +1,28 @@ +/********************************** (C) COPYRIGHT ******************************* + * File Name : system_ch32x035.h + * Author : WCH + * Version : V1.0.0 + * Date : 2023/04/06 + * Description : CH32X035 Device Peripheral Access Layer System Header File. + ********************************************************************************* + * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. + * Attention: This software (modified or not) and binary are used for + * microcontroller manufactured by Nanjing Qinheng Microelectronics. + *******************************************************************************/ +#ifndef __SYSTEM_CH32X035_H +#define __SYSTEM_CH32X035_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t SystemCoreClock; + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch32x035/wch-riscv.cfg b/hw/bsp/ch32x035/wch-riscv.cfg new file mode 100644 index 0000000000..7051c5c941 --- /dev/null +++ b/hw/bsp/ch32x035/wch-riscv.cfg @@ -0,0 +1,12 @@ +adapter driver wlinke +adapter speed 6000 +transport select sdi + +set _CHIPNAME wch_riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001 +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 diff --git a/hw/bsp/family.json b/hw/bsp/family.json index e2ce8eb413..8805b90aa4 100644 --- a/hw/bsp/family.json +++ b/hw/bsp/family.json @@ -517,6 +517,24 @@ } } }, + "ch32x035": { + "weact_ch32x035_core_board": { + "cmake": { + "defines": {}, + "family_mcus": [ + "CH32X035" + ], + "mcu": "OPT_MCU_CH32X035", + "options": {}, + "portable": [ + "wch/dcd_ch32_usbfs.c" + ], + "roles": [ + "device" + ] + } + } + }, "ch583": { "ch582m_evt": { "cmake": { diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index af43dfb120..9bf52c504a 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -606,6 +606,15 @@ #define TUP_DCD_ENDPOINT_MAX 8 +#elif TU_CHECK_MCU(OPT_MCU_CH32X035) + #define TUP_USBIP_WCH_USBFS + + #if !defined(CFG_TUD_WCH_USBIP_USBFS) + #define CFG_TUD_WCH_USBIP_USBFS 1 + #endif + + #define TUP_DCD_ENDPOINT_MAX 8 + #elif TU_CHECK_MCU(OPT_MCU_CH32V20X) // v20x support both port0 FSDEV (USBD) and port1 USBFS #define TUP_USBIP_WCH_USBFS diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h index 5b037281fc..e73a8b3ae2 100644 --- a/src/portable/wch/ch32_usbfs_reg.h +++ b/src/portable/wch/ch32_usbfs_reg.h @@ -168,7 +168,7 @@ // CH58x's hardware AUTO_TOG does not stay in sync (notably across clear-stall and multi-packet // bulk transfers), causing data-toggle mismatch and bus resets. Drive the toggle manually in // the ISR instead. CH32V103/V20x/V307 keep AUTO_TOG (this macro is undefined for them). - #define CH32_USBFS_EP_MANUAL_TOG 1 + #define CH32_USBFS_EP4_MANUAL_TOG 1 // CH58x EP4 has no DMA register of its own: it overlays EP0's DMA region as // EP0[0:63] + EP4_OUT[64:127] + EP4_IN[128:191], so EP0 needs a 192-byte buffer. #define CH32_USBFS_EP4_SHARES_EP0 1 @@ -177,6 +177,13 @@ #define NVIC_EnableIRQ(n) PFIC_EnableIRQ(n) #define NVIC_DisableIRQ(n) PFIC_DisableIRQ(n) #endif +#elif CFG_TUSB_MCU == OPT_MCU_CH32X035 + #include + #define USBOTG_FS USBFSD + #define CH32_USBFS_EP_CTRL_COMBINED 1 + #define CH32_USBFS_EP4_MANUAL_TOG 1 + #define CH32_USBFS_EP4_SHARES_EP0 1 + #define USBHD_IRQn USBFS_IRQn #endif // CH32V20x/V30x/F20x USBFS gives endpoint 3 a 1023-byte isochronous packet (CH32FV2x_V3xRM ch23: diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c index ec521224e5..4762e682df 100644 --- a/src/portable/wch/dcd_ch32_usbfs.c +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -41,6 +41,25 @@ #define EP_DMA(ep) (*((ep) <= 3u ? &USBOTG_FS->EP_DMA_0_3[0].DMA + (ep) * 2u \ : (ep) == 4u ? &USBOTG_FS->EP_DMA_0_3[0].DMA \ : &USBOTG_FS->EP_DMA_5_7[0].DMA + ((ep) - 5u) * 2u)) + #elif CFG_TUSB_MCU == OPT_MCU_CH32X035 + // EP0-4 and EP5-7 are split, and EP4 has no DMA register + static inline volatile uint32_t* ch32_usbfs_ep_dma_reg(uint8_t ep) { + switch (ep) { + case 0: return &USBOTG_FS->UEP0_DMA; + case 1: return &USBOTG_FS->UEP1_DMA; + case 2: return &USBOTG_FS->UEP2_DMA; + case 3: return &USBOTG_FS->UEP3_DMA; + case 4: return &USBOTG_FS->UEP0_DMA; + case 5: return &USBOTG_FS->UEP5_DMA; + case 6: return &USBOTG_FS->UEP6_DMA; + default: return &USBOTG_FS->UEP7_DMA; + } + } + + // There's a gap between EP4 and EP5 registers. + #define EP_DMA(ep) (*ch32_usbfs_ep_dma_reg(ep)) + #define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * (ep) + ((ep) > 4 ? 24 : 0)]) + #define EP_CTRL(ep) ((&USBOTG_FS->UEP0_CTRL_H)[2 * (ep) + ((ep) > 4 ? 24 : 0)]) #else #define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep]) #define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep]) @@ -48,10 +67,9 @@ #define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep]) #endif -// Endpoint control register access. The newer USBFS IP (CH32V20x/V307/X035) has separate -// TX_CTRL and RX_CTRL bytes per endpoint; the older IP (CH32V103) has a single combined -// UEPn_CTRL register. These helpers hide the difference so the rest of the driver is shared. -// Values use the newer-IP encoding (USBFS_EP_T_*/USBFS_EP_R_*); the combined path remaps them. +// Map generic TX/RX control values to split or combined endpoint control registers. +// Combined: V103/CH58X/X035 +// Split: V20x/V307 #ifdef CH32_USBFS_EP_CTRL_COMBINED #ifndef EP_CTRL // parts with a custom register map (CH58X) define EP_CTRL directly in reg.h #define EP_CTRL(ep) EP_TX_CTRL(ep) // UEPn_TX_CTRL field aliases the combined UEPn_CTRL register @@ -83,7 +101,6 @@ static inline void ep_rx_set_response(uint8_t ep, uint8_t res) { EP_CTRL(ep) = (uint8_t) ((EP_CTRL(ep) & ~USBFS_EPC_R_RES_MASK) | ((res & USBFS_EP_R_RES_MASK) << USBFS_EPC_R_RES_SHIFT)); } - #define EP0_SETUP_RX_TOG USBFS_EP_R_TOG // combined IP: data/status stage after SETUP is DATA1 #else static inline void ep_tx_ctrl_set(uint8_t ep, uint8_t v) { EP_TX_CTRL(ep) = v; } static inline void ep_rx_ctrl_set(uint8_t ep, uint8_t v) { EP_RX_CTRL(ep) = v; } @@ -93,19 +110,13 @@ static inline void ep_rx_set_response(uint8_t ep, uint8_t res) { EP_RX_CTRL(ep) = (uint8_t) ((EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | res); } - #define EP0_SETUP_RX_TOG 0 #endif // Hardware auto data-toggle flag. Parts whose AUTO_TOG is reliable OR it into the EP setup so the -// controller flips DATA0/DATA1 itself; CH58x (CH32_USBFS_EP_MANUAL_TOG) leaves it clear and the +// controller flips DATA0/DATA1 itself; CH58x (CH32_USBFS_EP4_MANUAL_TOG) leaves it clear and the // ISR flips the toggle bit after each packet instead. -#ifdef CH32_USBFS_EP_MANUAL_TOG - #define EP_T_AUTO_TOG 0 - #define EP_R_AUTO_TOG 0 -#else - #define EP_T_AUTO_TOG USBFS_EP_T_AUTO_TOG - #define EP_R_AUTO_TOG USBFS_EP_R_AUTO_TOG -#endif +#define EP_T_AUTO_TOG USBFS_EP_T_AUTO_TOG +#define EP_R_AUTO_TOG USBFS_EP_R_AUTO_TOG /* private data */ struct usb_xfer { @@ -118,101 +129,101 @@ struct usb_xfer { static struct { bool ep0_tog; + uint8_t ep0_status_dir; bool isochronous[EP_MAX][2]; // per [ep][dir]: an ep number may be iso in one direction struct usb_xfer xfer[EP_MAX][2]; #ifdef CH32_USBFS_EP4_SHARES_EP0 - // CH58X buffers laid out by hand so EP0/EP4 don't burn two unused buffer[] slots. EP0 and EP4 + // CH58X/X035 buffers laid out by hand so EP0/EP4 don't burn two unused buffer[] slots. EP0 and EP4 // share one contiguous 192-byte DMA region (EP4 has no DMA register of its own): // EP0 [0:63] (half-duplex OUT+IN) + EP4 OUT [64:127] + EP4 IN [128:191]. Every other endpoint - // (incl. EP3, which is bulk-only here — CH58X has no isochronous support) gets a plain 128-byte - // OUT+IN buffer, so no oversized EP3 buffer is needed. - TU_ATTR_ALIGNED(4) uint8_t ep0_ep4_buffer[3 * 64]; - TU_ATTR_ALIGNED(4) uint8_t ep1_buffer[2][64]; - TU_ATTR_ALIGNED(4) uint8_t ep2_buffer[2][64]; - TU_ATTR_ALIGNED(4) uint8_t ep3_buffer[2][64]; - TU_ATTR_ALIGNED(4) uint8_t ep5_buffer[2][64]; - TU_ATTR_ALIGNED(4) uint8_t ep6_buffer[2][64]; - TU_ATTR_ALIGNED(4) uint8_t ep7_buffer[2][64]; + // gets a plain 128-byte OUT+IN buffer. + TU_ATTR_ALIGNED(4) union { + uint8_t ep0_ep4_buffer[3 * 64]; + struct { + uint8_t ep0_buffer[64]; + uint8_t ep4_buffer[2][64]; + }; + }; + TU_ATTR_ALIGNED(4) uint8_t buffer[6][2][64]; #else - // Every endpoint gets a 64-byte OUT + 64-byte IN buffer. - TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64]; #if CFG_TUD_WCH_USBFS_EP3_BUFSIZE > 64 - // ...except EP3, which supports full-speed iso packets up to 1023 B on CH32V20x/V30x/F20x, so its - // IN buffer is enlarged (OUT stays 64 B; an OUT transfer >64 B on EP3 would overwrite queued IN). + // EP3 uses the enlarged buffer below, so omit its regular 128-byte slot. + TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX - 1][2][64]; + // EP3 supports full-speed iso packets up to 1023 B on CH32V20x/V30x/F20x. Its IN buffer is + // enlarged; OUT stays 64 B, so an OUT transfer over 64 B would overwrite queued IN data. TU_ATTR_ALIGNED(4) struct { uint8_t out[64]; uint8_t in[CFG_TUD_WCH_USBFS_EP3_BUFSIZE]; uint8_t pad; } ep3_buffer; + #else + TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64]; #endif #endif } data; // DMA / copy buffer pointers per endpoint. The WCH USBFS buffer holds OUT (RX) at offset 0 and -// IN (TX) at +64; EP0 is half-duplex and reuses its OUT chunk for IN. On CH58X, EP0/EP4 share +// IN (TX) at +64; EP0 is half-duplex and reuses its OUT chunk for IN. On CH58X/X035, EP0/EP4 share // ep0_ep4_buffer and the regular endpoints use their own named buffer (see the struct above). #ifdef CH32_USBFS_EP4_SHARES_EP0 -// OUT base of the regular CH58X endpoints (EP1/2/3/5/6/7; EP0/EP4 share ep0_ep4_buffer). -static inline uint8_t* ch58x_ep_buffer(uint8_t ep) { +static inline uint8_t* ep_buffer(uint8_t ep, uint8_t dir) { switch (ep) { - case 1: return data.ep1_buffer[0]; - case 2: return data.ep2_buffer[0]; - case 3: return data.ep3_buffer[0]; - case 5: return data.ep5_buffer[0]; - case 6: return data.ep6_buffer[0]; - default: return data.ep7_buffer[0]; // ep == 7 + case 1: return data.buffer[0][dir]; + case 2: return data.buffer[1][dir]; + case 3: return data.buffer[2][dir]; + case 4: return data.ep4_buffer[dir]; + case 5: return data.buffer[3][dir]; + case 6: return data.buffer[4][dir]; + default: return data.buffer[5][dir]; // ep == 7 } } -#endif static inline uint32_t ep_dma_addr(uint8_t ep) { -#ifdef CH32_USBFS_EP4_SHARES_EP0 - if (ep == 0 || ep == 4) { return (uint32_t) &data.ep0_ep4_buffer[0]; } // EP4 shares EP0's DMA - return (uint32_t) ch58x_ep_buffer(ep); -#else - #if CFG_TUD_WCH_USBFS_EP3_BUFSIZE > 64 - if (ep == 3) { return (uint32_t) &data.ep3_buffer.out[0]; } // EP3 has an enlarged IN buffer - #endif - return (uint32_t) &data.buffer[ep][0]; -#endif + if (ep == 0 || ep == 4) { return (uint32_t) data.ep0_ep4_buffer; } // EP4 shares EP0's DMA + return (uint32_t) ep_buffer(ep, TUSB_DIR_OUT); } static inline uint8_t* ep_out_buf(uint8_t ep) { -#ifdef CH32_USBFS_EP4_SHARES_EP0 - if (ep == 0) { return &data.ep0_ep4_buffer[0]; } - if (ep == 4) { return &data.ep0_ep4_buffer[64]; } - return ch58x_ep_buffer(ep); -#else - #if CFG_TUD_WCH_USBFS_EP3_BUFSIZE > 64 - if (ep == 3) { return data.ep3_buffer.out; } - #endif - return data.buffer[ep][TUSB_DIR_OUT]; -#endif + if (ep == 0) { return data.ep0_buffer; } + return ep_buffer(ep, TUSB_DIR_OUT); } static inline uint8_t* ep_in_buf(uint8_t ep) { -#ifdef CH32_USBFS_EP4_SHARES_EP0 - if (ep == 0) { return &data.ep0_ep4_buffer[0]; } // EP0 half-duplex: IN reuses OUT chunk - if (ep == 4) { return &data.ep0_ep4_buffer[128]; } - return ch58x_ep_buffer(ep) + 64; // IN at +64 within the endpoint's 128-byte buffer + if (ep == 0) { return data.ep0_buffer; } // EP0 half-duplex: IN reuses OUT chunk + return ep_buffer(ep, TUSB_DIR_IN); +} + +// EP4 on CH58X/X035 has no DMA register (shares EP0's); skip its EP_DMA() write. +static inline bool ep_shares_ep0_dma(uint8_t ep) { + return ep == 4; +} #else - if (ep == 0) { return data.buffer[0][TUSB_DIR_OUT]; } // EP0 half-duplex: IN reuses OUT chunk +static inline uint8_t* ep_buffer(uint8_t ep, uint8_t dir) { #if CFG_TUD_WCH_USBFS_EP3_BUFSIZE > 64 - if (ep == 3) { return data.ep3_buffer.in; } // enlarged IN buffer for full-speed iso + if (ep == 3) { return dir == TUSB_DIR_IN ? data.ep3_buffer.in : data.ep3_buffer.out; } + if (ep > 3) { return data.buffer[ep - 1][dir]; } #endif - return data.buffer[ep][TUSB_DIR_IN]; -#endif + return data.buffer[ep][dir]; +} + +static inline uint32_t ep_dma_addr(uint8_t ep) { + return (uint32_t) ep_buffer(ep, TUSB_DIR_OUT); +} + +static inline uint8_t* ep_out_buf(uint8_t ep) { + return ep_buffer(ep, TUSB_DIR_OUT); +} + +static inline uint8_t* ep_in_buf(uint8_t ep) { + if (ep == 0) { return ep_buffer(0, TUSB_DIR_OUT); } // EP0 half-duplex: IN reuses OUT chunk + return ep_buffer(ep, TUSB_DIR_IN); } -// EP4 on CH58X has no DMA register (shares EP0's); skip its EP_DMA() write. static inline bool ep_shares_ep0_dma(uint8_t ep) { -#ifdef CH32_USBFS_EP4_SHARES_EP0 - return ep == 4; -#else (void) ep; return false; -#endif } +#endif /* private helpers */ static void update_in(uint8_t rhport, uint8_t ep, bool force) { @@ -220,9 +231,9 @@ static void update_in(uint8_t rhport, uint8_t ep, bool force) { if (xfer->valid) { if (force || xfer->len) { size_t len = TU_MIN(xfer->max_size, xfer->len); -#if CFG_TUSB_MCU == OPT_MCU_CH583 - // Every CH58x endpoint buffer is 64 bytes; cap the copy so an iso mps a class mistakenly set - // larger can't write past the buffer into a neighbouring endpoint's. +#if CFG_TUSB_MCU == OPT_MCU_CH583 || CFG_TUSB_MCU == OPT_MCU_CH32X035 + // Every CH58x/X035 endpoint buffer is 64 bytes; cap the copy so a bad iso mps cannot write + // past the buffer into a neighbouring endpoint's. len = TU_MIN(len, 64u); #endif memcpy(ep_in_buf(ep), xfer->buffer, len); @@ -233,7 +244,6 @@ static void update_in(uint8_t rhport, uint8_t ep, bool force) { EP_TX_LEN(ep) = len; if (ep == 0) { ep_tx_ctrl_set(0, USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0)); - data.ep0_tog = !data.ep0_tog; } else if (data.isochronous[ep][TUSB_DIR_IN]) { ep_tx_set_response(ep, USBFS_EP_T_RES_NYET); } else { @@ -255,7 +265,7 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) { struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_OUT]; if (xfer->valid) { size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len)); -#if CFG_TUSB_MCU == OPT_MCU_CH583 +#if CFG_TUSB_MCU == OPT_MCU_CH583 || CFG_TUSB_MCU == OPT_MCU_CH32X035 len = TU_MIN(len, 64u); // cap to the 64-byte EP buffer (see update_in) #endif memcpy(xfer->buffer, ep_out_buf(ep), len); @@ -269,7 +279,7 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) { } if (ep == 0) { - ep_rx_set_response(0, USBFS_EP_R_RES_NAK); + ep_rx_ctrl_set(0, USBFS_EP_R_RES_NAK | (data.ep0_tog ? USBFS_EP_R_TOG : 0)); } else { uint8_t rx_res = data.isochronous[ep][TUSB_DIR_OUT] ? USBFS_EP_R_RES_NYET : (xfer->valid ? USBFS_EP_R_RES_ACK : USBFS_EP_R_RES_NAK); @@ -307,10 +317,9 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { // enable other endpoints but NAK everything USBOTG_FS->UEP4_1_MOD = 0xCC; USBOTG_FS->UEP2_3_MOD = 0xCC; -#if CFG_TUSB_MCU == OPT_MCU_CH583 +#if CFG_TUSB_MCU == OPT_MCU_CH583 || CFG_TUSB_MCU == OPT_MCU_CH32X035 // CH58X: a single mode register enables EP5/6/7 RX+TX (different bit layout than CH32). - USBOTG_FS->UEP567_MOD = RB_UEP5_RX_EN | RB_UEP5_TX_EN | RB_UEP6_RX_EN | RB_UEP6_TX_EN | - RB_UEP7_RX_EN | RB_UEP7_TX_EN; + USBOTG_FS->UEP567_MOD = 0x3F; #else USBOTG_FS->UEP5_6_MOD = 0xCC; USBOTG_FS->UEP7_MOD = 0x0C; @@ -334,35 +343,33 @@ void dcd_int_handler(uint8_t rhport) { switch (token) { case PID_OUT: { - // Drop an OUT packet whose data toggle doesn't match what we expect -- a host retransmit - // after a lost ACK, or a host that doesn't alternate DATA0/DATA1. The hardware auto-toggle - // does not reject these on its own, so the check is needed on every variant. EP0 keeps its - // own toggle via the SETUP/status flow and is exempt; isochronous is DATA0-only (no toggle), - // so its packets must not be toggle-checked. - if (ep != 0 && !data.isochronous[ep][TUSB_DIR_OUT] && !(int_st & USBFS_INT_ST_TOG_OK)) { break; } -#ifdef CH32_USBFS_EP_MANUAL_TOG - // CH58x has no hardware auto-toggle: advance the expected RX toggle after each accepted packet - // (EP0 included -- it also has no auto-toggle and a control-OUT data stage can span packets). - // Iso endpoints are DATA0-only, so leave them alone (matches the PID_IN path). - if (!data.isochronous[ep][TUSB_DIR_OUT]) { EP_CTRL(ep) ^= USBFS_EPC_R_TOG; } + // A lost ACK makes the host retry the previous DATA PID. Drop the duplicate before it + // reaches the transfer state and before advancing the expected toggle. EP0 owns its + // toggle in ep0_tog on every variant; isochronous endpoints are DATA0-only (no toggle). + if (!data.isochronous[ep][TUSB_DIR_OUT] && !(int_st & USBFS_INT_ST_TOG_OK)) { break; } + if (ep == 0) { data.ep0_tog = !data.ep0_tog; } +#ifdef CH32_USBFS_EP4_MANUAL_TOG + // Advance the expected EP4 RX toggle after each accepted packet. Iso endpoints are DATA0-only, + if (ep == 4 && !data.isochronous[ep][TUSB_DIR_OUT]) { EP_CTRL(ep) ^= USBFS_EPC_R_TOG; } #endif update_out(rhport, ep, rx_len); break; } case PID_IN: -#ifdef CH32_USBFS_EP_MANUAL_TOG - // Manual toggle: flip the TX toggle after each ACK'd IN packet (EP0 manages its own). + if (ep == 0) { data.ep0_tog = !data.ep0_tog; } +#ifdef CH32_USBFS_EP4_MANUAL_TOG + // Manual toggle: flip the TX toggle after each ACK'd IN packet for EP4. // Isochronous transfers are DATA0-only (no toggle), so leave iso endpoints alone. - if (ep != 0 && !data.isochronous[ep][TUSB_DIR_IN]) { EP_CTRL(ep) ^= USBFS_EPC_T_TOG; } + if (ep == 4 && !data.isochronous[ep][TUSB_DIR_IN]) { EP_CTRL(ep) ^= USBFS_EPC_T_TOG; } #endif update_in(rhport, ep, false); break; case PID_SETUP: // setup clears stall - ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK); data.ep0_tog = true; + ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK | USBFS_EP_T_TOG); // A new SETUP supersedes any control transfer still in flight; drop its stale EP0 state so a // spurious EP0 IN/OUT can't run update_in()/update_out() against the previous request. data.xfer[0][TUSB_DIR_OUT].valid = false; @@ -370,8 +377,10 @@ void dcd_int_handler(uint8_t rhport) { uint8_t *ep0_out = ep_out_buf(0); const tusb_control_request_t *setup = (const tusb_control_request_t *)ep0_out; - // EP0_SETUP_RX_TOG arms the data/status stage at DATA1 on the combined-control IP - ep_rx_ctrl_set(0, ((setup->wLength == 0) ? USBFS_EP_R_RES_ACK : USBFS_EP_R_RES_NAK) | EP0_SETUP_RX_TOG); + data.ep0_status_dir = (setup->wLength && setup->bmRequestType_bit.direction == TUSB_DIR_IN) ? TUSB_DIR_OUT + : TUSB_DIR_IN; + // The first transaction of either the data or status stage always uses DATA1. + ep_rx_ctrl_set(0, ((setup->wLength == 0) ? USBFS_EP_R_RES_ACK : USBFS_EP_R_RES_NAK) | USBFS_EP_R_TOG); dcd_event_setup_received(rhport, ep0_out, true); break; @@ -531,6 +540,10 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to // read-modify-write of the (combined) EP control register, which the ISR also RMWs to flip the // manual data toggle; re-enabling before they run lets a transfer IRQ clobber that toggle. dcd_int_disable(rhport); + // The status stage is opposite the data direction (or IN when there is no data) and always + // uses DATA1. A zero-length data packet remains in the data direction and keeps its sequence. + if (ep == 0 && total_bytes == 0 && dir == data.ep0_status_dir) { data.ep0_tog = true; } + xfer->valid = true; xfer->buffer = buffer; xfer->len = total_bytes; @@ -540,7 +553,11 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to update_in(rhport, ep, true); } else { uint8_t rx_res = data.isochronous[ep][TUSB_DIR_OUT] ? USBFS_EP_R_RES_NYET : USBFS_EP_R_RES_ACK; - ep_rx_set_response(ep, rx_res); + if (ep == 0) { + ep_rx_ctrl_set(0, rx_res | (data.ep0_tog ? USBFS_EP_R_TOG : 0)); + } else { + ep_rx_set_response(ep, rx_res); + } } dcd_int_enable(rhport); return true; @@ -583,8 +600,14 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { uint8_t res = data.xfer[ep][TUSB_DIR_OUT].valid ? (data.isochronous[ep][TUSB_DIR_OUT] ? USBFS_EP_R_RES_NYET : USBFS_EP_R_RES_ACK) : USBFS_EP_R_RES_NAK; +#ifdef CH32_USBFS_EP4_MANUAL_TOG + ep_rx_ctrl_set(ep, res); +#endif ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | res); } else { +#ifdef CH32_USBFS_EP4_MANUAL_TOG + ep_tx_ctrl_set(ep, USBFS_EP_T_RES_NAK); +#endif ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK); } } diff --git a/src/tusb_option.h b/src/tusb_option.h index 861c5e586b..b02f30b69d 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -178,6 +178,7 @@ #define OPT_MCU_CH32V103 2230 ///< WCH CH32V103 #define OPT_MCU_CH583 2240 ///< WCH CH583 #define OPT_MCU_CH582 OPT_MCU_CH583 ///< WCH CH582 (alias, same USB IP as CH583) +#define OPT_MCU_CH32X035 2250 ///< WCH CH32X035 // NXP LPC MCX #define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series diff --git a/tools/get_deps.py b/tools/get_deps.py index 553d206ce5..bfd00e480e 100755 --- a/tools/get_deps.py +++ b/tools/get_deps.py @@ -265,6 +265,9 @@ 'hw/mcu/wch/ch583': ['https://github.com/openwch/ch583.git', 'bd508ad7ceed48377619837051412a651952857f', 'ch583'], + 'hw/mcu/wch/ch32x035': ['https://github.com/openwch/ch32x035.git', + 'c6253942b29ff2a3f5e2829182a874afffa45881', + 'ch32x035'], 'hw/mcu/artery/at32f403a_407': ['https://github.com/ArteryTek/AT32F403A_407_Firmware_Library.git', 'f2cb360c3d28fada76b374308b8c4c61d37a090b', 'at32f403a_407'],