diff options
| author | Ha Thach <[email protected]> | 2026-06-22 22:20:29 +0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-06-22 22:20:29 +0700 |
| commit | cd3561bf158afd5a5718904b8139a338d1e3b67c (patch) | |
| tree | 4b9d46e8b6397ba050d7fba159a67efc0d511eca | |
| parent | 299c0a55629691f6bbded895a4be377633e815ab (diff) | |
| parent | 706e4a5daaf8bbb55f0a7d00b69d4bc3c4cd70eb (diff) | |
Merge pull request #3515 from alt-0191/ch58x
Add support for WCH ch582/583 series
47 files changed, 1303 insertions, 32 deletions
diff --git a/.github/workflows/ci_set_matrix.py b/.github/workflows/ci_set_matrix.py index f4d25fd37..dc0d3871f 100755 --- a/.github/workflows/ci_set_matrix.py +++ b/.github/workflows/ci_set_matrix.py @@ -30,6 +30,7 @@ family_list = { "ch32v10x": ["riscv-gcc"], "ch32v20x": ["riscv-gcc"], "ch32v30x": ["riscv-gcc"], + "ch583": ["riscv-gcc"], "da1469x": ["arm-gcc"], "fomu": ["riscv-gcc"], "ft9xx": ["ft9xx-gcc"], diff --git a/README.rst b/README.rst index 38a77f8f6..ea71e1adc 100644 --- a/README.rst +++ b/README.rst @@ -310,6 +310,10 @@ Supported CPUs | | CH32V20x | 🟡 | | ❌ | stm32_fsdev/ch32_usbfs | ISO data loss [2]_ | | +-----------------------------+--------+------+-----------+------------------------+--------------------+ | | CH32V305, CH32V307 | 🟡 | | ✅ | ch32_usbfs/hs | ISO data loss [2]_ | +| +-----------------------------+--------+------+-----------+------------------------+--------------------+ +| | CH32V103 | 🟡 | | ❌ | ch32_usbfs | ISO data loss [2]_ | +| +-----------------------------+--------+------+-----------+------------------------+--------------------+ +| | CH583, CH582 | 🟡 | | ❌ | ch32_usbfs | Device only | +--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+ Table Legend diff --git a/docs/reference/boards.rst b/docs/reference/boards.rst index b0e8bffaa..8a83496a4 100644 --- a/docs/reference/boards.rst +++ b/docs/reference/boards.rst @@ -370,4 +370,5 @@ ch32v203g_r0_1v0 CH32V203G-R0-1v0 ch32v20x https://github.com/openwch/ch32v20 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 +yd-ch582m yd-ch582m ch583 http://vcc-gnd.com ================ ================ ======== ===================================================================== ====== diff --git a/examples/device/audio_4_channel_mic/skip.txt b/examples/device/audio_4_channel_mic/skip.txt index 157605df1..3ca433c08 100644 --- a/examples/device/audio_4_channel_mic/skip.txt +++ b/examples/device/audio_4_channel_mic/skip.txt @@ -3,3 +3,4 @@ mcu:SAME5X mcu:SAMG family:broadcom_64bit family:espressif +mcu:CH583 diff --git a/examples/device/audio_4_channel_mic_freertos/skip.txt b/examples/device/audio_4_channel_mic_freertos/skip.txt index 61a7b0605..1fd6b4b8a 100644 --- a/examples/device/audio_4_channel_mic_freertos/skip.txt +++ b/examples/device/audio_4_channel_mic_freertos/skip.txt @@ -2,6 +2,7 @@ mcu:CH32F20X mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/audio_test/skip.txt b/examples/device/audio_test/skip.txt index 65b137814..42394bb11 100644 --- a/examples/device/audio_test/skip.txt +++ b/examples/device/audio_test/skip.txt @@ -2,3 +2,4 @@ mcu:SAMD11 mcu:SAME5X mcu:SAMG family:espressif +mcu:CH583 diff --git a/examples/device/audio_test_freertos/skip.txt b/examples/device/audio_test_freertos/skip.txt index 386a0cdfb..660bacd25 100644 --- a/examples/device/audio_test_freertos/skip.txt +++ b/examples/device/audio_test_freertos/skip.txt @@ -2,6 +2,7 @@ mcu:CH32F20X mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 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 65b137814..42394bb11 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:SAME5X mcu:SAMG family:espressif +mcu:CH583 diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt index 429c62d93..48781de84 100644 --- a/examples/device/cdc_msc_freertos/skip.txt +++ b/examples/device/cdc_msc_freertos/skip.txt @@ -2,6 +2,7 @@ mcu:CH32F20X mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/cdc_uac2/skip.txt b/examples/device/cdc_uac2/skip.txt index a2a76af0e..db1d5b80b 100644 --- a/examples/device/cdc_uac2/skip.txt +++ b/examples/device/cdc_uac2/skip.txt @@ -6,3 +6,4 @@ mcu:SAME5X mcu:SAMG board:stm32l052dap52 family:espressif +mcu:CH583 diff --git a/examples/device/dfu_runtime/skip.txt b/examples/device/dfu_runtime/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/dfu_runtime/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/hid_boot_interface/skip.txt b/examples/device/hid_boot_interface/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/hid_boot_interface/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/hid_composite/skip.txt b/examples/device/hid_composite/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/hid_composite/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt index d2ee8d636..97d8e168b 100644 --- a/examples/device/hid_composite_freertos/skip.txt +++ b/examples/device/hid_composite_freertos/skip.txt @@ -2,6 +2,7 @@ mcu:CH32F20X mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/hid_generic_inout/skip.txt b/examples/device/hid_generic_inout/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/hid_generic_inout/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/hid_multiple_interface/skip.txt b/examples/device/hid_multiple_interface/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/hid_multiple_interface/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/midi_test/skip.txt b/examples/device/midi_test/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/midi_test/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/examples/device/midi_test_freertos/skip.txt b/examples/device/midi_test_freertos/skip.txt index d2ee8d636..97d8e168b 100644 --- a/examples/device/midi_test_freertos/skip.txt +++ b/examples/device/midi_test_freertos/skip.txt @@ -2,6 +2,7 @@ mcu:CH32F20X mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 mcu:CXD56 mcu:F1C100S mcu:GD32VF103 diff --git a/examples/device/uac2_headset/skip.txt b/examples/device/uac2_headset/skip.txt index a2a76af0e..db1d5b80b 100644 --- a/examples/device/uac2_headset/skip.txt +++ b/examples/device/uac2_headset/skip.txt @@ -6,3 +6,4 @@ mcu:SAME5X mcu:SAMG board:stm32l052dap52 family:espressif +mcu:CH583 diff --git a/examples/device/uac2_speaker_fb/skip.txt b/examples/device/uac2_speaker_fb/skip.txt index 234f1ebed..0c7339c65 100644 --- a/examples/device/uac2_speaker_fb/skip.txt +++ b/examples/device/uac2_speaker_fb/skip.txt @@ -6,3 +6,4 @@ mcu:SAME5X mcu:SAMG board:stm32l052dap52 family:broadcom_64bit +mcu:CH583 diff --git a/examples/device/video_capture/skip.txt b/examples/device/video_capture/skip.txt index cb0c7d2e6..5a7a1d00e 100644 --- a/examples/device/video_capture/skip.txt +++ b/examples/device/video_capture/skip.txt @@ -1,5 +1,6 @@ mcu:CH32V103 mcu:CH32V20X +mcu:CH583 mcu:MSP430x5xx mcu:NUC121 mcu:SAMD11 diff --git a/examples/device/video_capture_2ch/skip.txt b/examples/device/video_capture_2ch/skip.txt index af3b0de04..c37205b8c 100644 --- a/examples/device/video_capture_2ch/skip.txt +++ b/examples/device/video_capture_2ch/skip.txt @@ -5,6 +5,7 @@ mcu:GD32VF103 mcu:CH32V103 mcu:CH32V20X mcu:CH32V307 +mcu:CH583 mcu:STM32L0 family:espressif board:curiosity_nano diff --git a/examples/device/webusb_serial/skip.txt b/examples/device/webusb_serial/skip.txt new file mode 100644 index 000000000..2c6c2a64c --- /dev/null +++ b/examples/device/webusb_serial/skip.txt @@ -0,0 +1 @@ +family:espressif diff --git a/hw/bsp/ch583/boards/ch582m_evt/board.cmake b/hw/bsp/ch583/boards/ch582m_evt/board.cmake new file mode 100644 index 000000000..4129c4550 --- /dev/null +++ b/hw/bsp/ch583/boards/ch582m_evt/board.cmake @@ -0,0 +1,5 @@ +set(LD_FLASH_SIZE 448K) +set(LD_RAM_SIZE 32K) + +function(update_board TARGET) +endfunction() diff --git a/hw/bsp/ch583/boards/ch582m_evt/board.h b/hw/bsp/ch583/boards/ch582m_evt/board.h new file mode 100644 index 000000000..c3483bf17 --- /dev/null +++ b/hw/bsp/ch583/boards/ch582m_evt/board.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* metadata: + name: CH582M-EVT evaluation board + url: https://www.wch-ic.com/products/CH582.html +*/ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// LED: PB4 on CH582M-EVT +#define LED_PIN GPIO_Pin_4 +#define LED_STATE_ON 0 + +// Directly reuse BOOT pin as user button +#define BUTTON_PIN GPIO_Pin_22 +#define BUTTON_STATE_ACTIVE 0 + +// UART: UART1 TX=PA9, RX=PA8 +#define CFG_BOARD_UART_BAUDRATE 115200 + +// Device only on USB1 (rhport 0). CH58x host / USB2 is not supported by the shared usbfs dcd; +// BOARD_TUH_RHPORT is kept commented out to ease re-adding host later. +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif +// #ifndef BOARD_TUH_RHPORT +// #define BOARD_TUH_RHPORT 1 +// #endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch583/boards/ch582m_evt/board.mk b/hw/bsp/ch583/boards/ch582m_evt/board.mk new file mode 100644 index 000000000..a13979799 --- /dev/null +++ b/hw/bsp/ch583/boards/ch582m_evt/board.mk @@ -0,0 +1,3 @@ +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=448K \ + -Wl,--defsym=__RAM_SIZE=32K \ diff --git a/hw/bsp/ch583/boards/yd-ch582m/board.cmake b/hw/bsp/ch583/boards/yd-ch582m/board.cmake new file mode 100644 index 000000000..4129c4550 --- /dev/null +++ b/hw/bsp/ch583/boards/yd-ch582m/board.cmake @@ -0,0 +1,5 @@ +set(LD_FLASH_SIZE 448K) +set(LD_RAM_SIZE 32K) + +function(update_board TARGET) +endfunction() diff --git a/hw/bsp/ch583/boards/yd-ch582m/board.h b/hw/bsp/ch583/boards/yd-ch582m/board.h new file mode 100644 index 000000000..0da5747bc --- /dev/null +++ b/hw/bsp/ch583/boards/yd-ch582m/board.h @@ -0,0 +1,61 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* metadata: + name: yd-ch582m from vcc-gnd studio + url: http://vcc-gnd.com/ +*/ + +#ifndef BOARD_H_ +#define BOARD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// LED: PB4 on yd-ch582m board +#define LED_PIN GPIO_Pin_4 +#define LED_STATE_ON 0 + +// Directly reuse BOOT pin as user button +#define BUTTON_PIN GPIO_Pin_22 +#define BUTTON_STATE_ACTIVE 0 + +// UART: UART1 TX=PA9, RX=PA8 +#define CFG_BOARD_UART_BAUDRATE 115200 + +// Device only on USB1 (rhport 0). CH58x host / USB2 is not supported by the shared usbfs dcd; +// BOARD_TUH_RHPORT is kept commented out to ease re-adding host later. +#ifndef BOARD_TUD_RHPORT +#define BOARD_TUD_RHPORT 0 +#endif +// #ifndef BOARD_TUH_RHPORT +// #define BOARD_TUH_RHPORT 1 +// #endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/hw/bsp/ch583/boards/yd-ch582m/board.mk b/hw/bsp/ch583/boards/yd-ch582m/board.mk new file mode 100644 index 000000000..a13979799 --- /dev/null +++ b/hw/bsp/ch583/boards/yd-ch582m/board.mk @@ -0,0 +1,3 @@ +LDFLAGS += \ + -Wl,--defsym=__FLASH_SIZE=448K \ + -Wl,--defsym=__RAM_SIZE=32K \ diff --git a/hw/bsp/ch583/ch583_it.c b/hw/bsp/ch583/ch583_it.c new file mode 100644 index 000000000..8479c537a --- /dev/null +++ b/hw/bsp/ch583/ch583_it.c @@ -0,0 +1,37 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "ch583_it.h" + +// NMI exception handler +__INTERRUPT __HIGH_CODE void NMI_Handler(void) { + while (1) {} +} + +// Hard Fault exception handler +__INTERRUPT __HIGH_CODE void HardFault_Handler(void) { + while (1) {} +} diff --git a/hw/bsp/ch583/ch583_it.h b/hw/bsp/ch583/ch583_it.h new file mode 100644 index 000000000..29d175403 --- /dev/null +++ b/hw/bsp/ch583/ch583_it.h @@ -0,0 +1,46 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef CH583_IT_H_ +#define CH583_IT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "CH58x_common.h" + +void NMI_Handler(void); +void HardFault_Handler(void); +void USB_IRQHandler(void); +// void USB2_IRQHandler(void); // host on USB2 (rhport 1) — re-add together with the host driver +void SysTick_Handler(void); + +#ifdef __cplusplus +} +#endif + +#endif /* CH583_IT_H_ */ diff --git a/hw/bsp/ch583/debug_uart.c b/hw/bsp/ch583/debug_uart.c new file mode 100644 index 000000000..850e40718 --- /dev/null +++ b/hw/bsp/ch583/debug_uart.c @@ -0,0 +1,86 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "debug_uart.h" +#include "CH58x_common.h" + +//--------------------------------------------------------------------+ +// Ring buffer based UART TX for non-blocking writes +//--------------------------------------------------------------------+ + +#define UART_RINGBUFFER_SIZE_TX 128 +#define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX - 1) + +static char tx_buf[UART_RINGBUFFER_SIZE_TX]; +static uint32_t tx_produce; +static volatile uint32_t tx_consume; + +void uart_write(char c) { + uint32_t tx_produce_next = (tx_produce + 1) & UART_RINGBUFFER_MASK_TX; + + // If the ring buffer is full, drain it here as the FIFO frees up: nothing else advances + // tx_consume between uart_write() calls, so a plain spin would deadlock on a >buffer-size burst. + while (tx_produce_next == tx_consume) { + if (R8_UART1_LSR & RB_LSR_TX_FIFO_EMP) { + R8_UART1_THR = tx_buf[tx_consume]; + tx_consume = (tx_consume + 1) & UART_RINGBUFFER_MASK_TX; + } + } + + // If UART TX FIFO is empty and no pending data, send directly + if ((tx_consume == tx_produce) && (R8_UART1_LSR & RB_LSR_TX_FIFO_EMP)) { + R8_UART1_THR = c; + } else { + tx_buf[tx_produce] = c; + tx_produce = tx_produce_next; + } +} + +void uart_sync(void) { + // Wait for ring buffer to drain + while (tx_consume != tx_produce) { + if (R8_UART1_LSR & RB_LSR_TX_FIFO_EMP) { + R8_UART1_THR = tx_buf[tx_consume]; + tx_consume = (tx_consume + 1) & UART_RINGBUFFER_MASK_TX; + } + } + // Wait for last byte to finish transmitting + while (!(R8_UART1_LSR & RB_LSR_TX_ALL_EMP)) {} +} + +void usart_printf_init(uint32_t baudrate) { + tx_produce = 0; + tx_consume = 0; + + // Configure UART1 pins: TX=PA9, RX=PA8 + GPIOA_SetBits(GPIO_Pin_9); + GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); + GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); + + // Init UART1 with specified baud rate + UART1_DefInit(); + UART1_BaudRateCfg(baudrate); +} diff --git a/hw/bsp/ch583/debug_uart.h b/hw/bsp/ch583/debug_uart.h new file mode 100644 index 000000000..44c3e7948 --- /dev/null +++ b/hw/bsp/ch583/debug_uart.h @@ -0,0 +1,44 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef DEBUG_UART_H_ +#define DEBUG_UART_H_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +void uart_write(char c); +void uart_sync(void); +void usart_printf_init(uint32_t baudrate); + +#ifdef __cplusplus +} +#endif + +#endif /* DEBUG_UART_H_ */ diff --git a/hw/bsp/ch583/family.c b/hw/bsp/ch583/family.c new file mode 100644 index 000000000..716e2b76b --- /dev/null +++ b/hw/bsp/ch583/family.c @@ -0,0 +1,194 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/* metadata: + manufacturer: WCH +*/ + +// WCH SDK's DEBUG macro enables a _write() that conflicts with TinyUSB's. +// TinyUSB uses UART1 for printf via debug_uart.c, so DEBUG is not needed. +// If you need a different UART or want to keep SDK's DEBUG, modify +// debug_uart.c (TinyUSB side) or CH58x_sys.c (SDK side) to remove one _write(). +// If done, remove this #error to continue. +#ifdef DEBUG + #error "Remove the DEBUG macro from preprocessor defines to avoid " \ + "duplicate _write() between WCH SDK and TinyUSB. " \ + "TinyUSB uses UART1 by default (see debug_uart.c)." +#endif + +#include "debug_uart.h" +#include "CH58x_common.h" +#include "ch583_it.h" + +#include "bsp/board_api.h" +#include "board.h" + +//--------------------------------------------------------------------+ +// Forward USB interrupt events to TinyUSB IRQ Handler +//--------------------------------------------------------------------+ + +// Device only: the shared dcd_ch32_usbfs.c drives USB0 (rhport 0). CH58x's second controller +// (USB2 / rhport 1) was driven by the now-removed ch58x host driver; its handler is kept +// commented out below to ease re-adding host support. +__INTERRUPT __HIGH_CODE void USB_IRQHandler(void) { + tusb_int_handler(0, true); +} + +// __INTERRUPT __HIGH_CODE void USB2_IRQHandler(void) { +// tusb_int_handler(1, true); +// } + +//--------------------------------------------------------------------+ +// SysTick +//--------------------------------------------------------------------+ + +#if CFG_TUSB_OS == OPT_OS_NONE +volatile uint32_t system_ticks = 0; + +__INTERRUPT __HIGH_CODE void SysTick_Handler(void) { + SysTick->SR = 0; + system_ticks++; +} + +uint32_t tusb_time_millis_api(void) { + return system_ticks; +} +#endif + +//--------------------------------------------------------------------+ +// Board Init +//--------------------------------------------------------------------+ + +void board_init(void) { + // Disable interrupts during init + PFIC_DisableAllIRQ(); + + // Set system clock to PLL 60MHz (default for CH582) + SetSysClock(CLK_SOURCE_PLL_60MHz); + +#if CFG_TUSB_OS == OPT_OS_NONE + SysTick_Config(GetSysClock() / 1000); +#endif + + // UART1 init for debug output +#ifdef CFG_BOARD_UART_BAUDRATE + usart_printf_init(CFG_BOARD_UART_BAUDRATE); +#endif + + // LED +#ifdef LED_PORT_IS_A + GPIOA_ModeCfg(LED_PIN, GPIO_ModeOut_PP_5mA); +#else + GPIOB_ModeCfg(LED_PIN, GPIO_ModeOut_PP_5mA); +#endif + + // Button +#ifdef BUTTON_PIN + #ifdef BUTTON_PORT_IS_A + GPIOA_ModeCfg(BUTTON_PIN, GPIO_ModeIN_PU); + #else + GPIOB_ModeCfg(BUTTON_PIN, GPIO_ModeIN_PU); + #endif +#endif + + // Device only on USB0 (rhport 0): enable analog function for USB1 D+/D- and assert its D+ + // pull-up. The shared dcd_ch32_usbfs.c drives USB0; CH58x host / USB2 (rhport 1) is unsupported + // here — to re-add host, also OR in RB_PIN_USB2_IE below. + R16_PIN_ANALOG_IE |= RB_PIN_USB_IE; // | RB_PIN_USB2_IE (re-add for host on USB2) +#if CFG_TUD_ENABLED + R16_PIN_ANALOG_IE |= RB_PIN_USB_DP_PU; +#endif + + // Keep USB clock active during sleep + R8_SLP_CLK_OFF1 &= ~RB_SLP_CLK_USB; + + // Enable interrupts globally + PFIC_EnableAllIRQ(); + + board_delay(2); +} + +//--------------------------------------------------------------------+ +// Board porting API +//--------------------------------------------------------------------+ + +void board_led_write(bool state) { +#ifdef LED_PORT_IS_A + if (state ^ LED_STATE_ON) { + GPIOA_ResetBits(LED_PIN); + } else { + GPIOA_SetBits(LED_PIN); + } +#else + if (state ^ LED_STATE_ON) { + GPIOB_ResetBits(LED_PIN); + } else { + GPIOB_SetBits(LED_PIN); + } +#endif +} + +uint32_t board_button_read(void) { +#ifdef BUTTON_PIN + #ifdef BUTTON_PORT_IS_A + return BUTTON_STATE_ACTIVE == (GPIOA_ReadPortPin(BUTTON_PIN) ? 1 : 0); + #else + return BUTTON_STATE_ACTIVE == (GPIOB_ReadPortPin(BUTTON_PIN) ? 1 : 0); + #endif +#else + return 0; +#endif +} + +// CH58x has no memory-mapped unique-ID register (unlike ch32v10x/v20x at 0x1FFFF7E8), but the +// factory programs a unique 6-byte MAC address into FlashROM (it is a BLE part). GetMACAddress() +// reads it via the ISP ROM command FLASH_EEPROM_CMD, which is provided by libISP583.a. +size_t board_get_unique_id(uint8_t id[], size_t max_len) { + // FLASH_EEPROM_CMD writes word-granular and requires a 4-byte-aligned buffer (CH58x_flash.c); + // size 8 matches the SDK's GET_UNIQUE_ID buffer (6 MAC bytes + 2 it pads), so the read can't + // run past the end whether it returns 6 or a full 8. + TU_ATTR_ALIGNED(4) uint8_t mac[8]; + GetMACAddress(mac); + size_t len = TU_MIN(max_len, (size_t) 6); // the 6-byte MAC is the unique part + memcpy(id, mac, len); + return len; +} + +int board_uart_read(uint8_t* buf, int len) { + (void) buf; + (void) len; + return 0; +} + +int board_uart_write(void const* buf, int len) { + int txsize = len; + const char* bufc = (const char*) buf; + while (txsize--) { + uart_write(*bufc++); + } + uart_sync(); + return len; +} diff --git a/hw/bsp/ch583/family.cmake b/hw/bsp/ch583/family.cmake new file mode 100644 index 000000000..a379298e5 --- /dev/null +++ b/hw/bsp/ch583/family.cmake @@ -0,0 +1,108 @@ +include_guard() + +set(SDK_DIR ${TOP}/hw/mcu/wch/ch583) +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 CH583 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/ch582.ld) +endif () +set(LD_FILE_Clang ${LD_FILE_GNU}) +if (NOT DEFINED STARTUP_FILE_GNU) + set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_CH583.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}/StdPeriphDriver/CH58x_gpio.c + ${SDK_SRC_DIR}/StdPeriphDriver/CH58x_clk.c + ${SDK_SRC_DIR}/StdPeriphDriver/CH58x_uart1.c + ${SDK_SRC_DIR}/StdPeriphDriver/CH58x_sys.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ch583_it.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_ch583.c + ) + target_include_directories(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/RVMSIS + ${SDK_SRC_DIR}/StdPeriphDriver/inc + ${CMAKE_CURRENT_FUNCTION_LIST_DIR} + ) + target_link_libraries(${BOARD_TARGET} PUBLIC + ${SDK_SRC_DIR}/StdPeriphDriver/libISP583.a + ) + target_compile_definitions(${BOARD_TARGET} PUBLIC + CFG_TUD_WCH_USBIP_USBFS=1 + FREQ_SYS=60000000 + DISK_LIB_ENABLE=0 + INT_SOFT + ) + + update_board(${BOARD_TARGET}) + + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_compile_options(${BOARD_TARGET} PUBLIC + -flto + -msmall-data-limit=16 + -mno-save-restore + -fmessage-length=0 + -fsigned-char + -Wno-error=strict-prototypes + ) + endif () +endfunction() + +#------------------------------------ +# Functions +#------------------------------------ +function(family_configure_example TARGET RTOS) + family_configure_common(${TARGET} ${RTOS}) + family_add_tinyusb(${TARGET} OPT_MCU_CH583) + + target_sources(${TARGET} PUBLIC + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c + ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/debug_uart.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 for CH58x") + 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_wch(${TARGET}) +endfunction() diff --git a/hw/bsp/ch583/family.mk b/hw/bsp/ch583/family.mk new file mode 100644 index 000000000..98d0f9337 --- /dev/null +++ b/hw/bsp/ch583/family.mk @@ -0,0 +1,59 @@ +# https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable +#CROSS_COMPILE ?= riscv32-unknown-elf- + +# Toolchain from https://nucleisys.com/download.php +#CROSS_COMPILE ?= riscv-nuclei-elf- + +# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack +CROSS_COMPILE ?= riscv-none-elf- + +SDK_DIR = hw/mcu/wch/ch583 +SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC + +include $(TOP)/$(BOARD_PATH)/board.mk +CPU_CORE ?= rv32imac-ilp32 + +CFLAGS += \ + -flto \ + -msmall-data-limit=16 \ + -mno-save-restore \ + -fmessage-length=0 \ + -fsigned-char \ + -DCFG_TUSB_MCU=OPT_MCU_CH583 \ + -DCFG_TUD_WCH_USBIP_USBFS=1 \ + -DFREQ_SYS=60000000 \ + -DDISK_LIB_ENABLE=0 \ + -DINT_SOFT \ + -Wno-error=strict-prototypes + +LDFLAGS += \ + -nostartfiles \ + --specs=nosys.specs --specs=nano.specs + +LIBS += $(TOP)/$(SDK_SRC_DIR)/StdPeriphDriver/libISP583.a + +SRC_C += \ + src/portable/wch/dcd_ch32_usbfs.c \ + $(SDK_SRC_DIR)/StdPeriphDriver/CH58x_gpio.c \ + $(SDK_SRC_DIR)/StdPeriphDriver/CH58x_clk.c \ + $(SDK_SRC_DIR)/StdPeriphDriver/CH58x_uart1.c \ + $(SDK_SRC_DIR)/StdPeriphDriver/CH58x_sys.c \ + $(FAMILY_PATH)/debug_uart.c \ + $(FAMILY_PATH)/ch583_it.c \ + $(FAMILY_PATH)/system_ch583.c + +SRC_S += \ + $(SDK_SRC_DIR)/Startup/startup_CH583.S + +INC += \ + $(TOP)/$(BOARD_PATH) \ + $(TOP)/$(SDK_SRC_DIR)/RVMSIS \ + $(TOP)/$(SDK_SRC_DIR)/StdPeriphDriver/inc + +LD_FILE ?= $(FAMILY_PATH)/linker/ch582.ld + +OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg +flash: flash-openocd-wch + +# For freeRTOS port source +FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V diff --git a/hw/bsp/ch583/linker/ch582.ld b/hw/bsp/ch583/linker/ch582.ld new file mode 100644 index 000000000..821998a49 --- /dev/null +++ b/hw/bsp/ch583/linker/ch582.ld @@ -0,0 +1,167 @@ +/* CH582 Linker Script for TinyUSB + * Based on WCH CH583 SDK Link.ld + * Supports parameterized flash/ram sizes via --defsym + */ + +/* Default sizes if not provided via --defsym */ +__flash_size = DEFINED(__FLASH_SIZE) ? __FLASH_SIZE : 448K; +__ram_size = DEFINED(__RAM_SIZE) ? __RAM_SIZE : 32K; +__stack_size = DEFINED(__STACK_SIZE) ? __STACK_SIZE : 2048; + +ENTRY( _start ) + +PROVIDE( _stack_size = __stack_size ); + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __flash_size + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __ram_size +} + +SECTIONS +{ + .init : + { + _sinit = .; + . = ALIGN(4); + KEEP(*(SORT_NONE(.init))) + . = ALIGN(4); + _einit = .; + } >FLASH AT>FLASH + + .highcodelalign : + { + . = ALIGN(4); + PROVIDE(_highcode_lma = .); + } >FLASH AT>FLASH + + .highcode : + { + . = ALIGN(4); + PROVIDE(_highcode_vma_start = .); + *(.vector); + KEEP(*(SORT_NONE(.vector_handler))) + *(.highcode); + *(.highcode.*); + . = ALIGN(4); + PROVIDE(_highcode_vma_end = .); + } >RAM AT>FLASH + + .text : + { + . = ALIGN(4); + KEEP(*(SORT_NONE(.handle_reset))) + *(.text) + *(.text.*) + *(.rodata) + *(.rodata*) + *(.sdata2.*) + *(.glue_7) + *(.glue_7t) + *(.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 + + .dlalign : + { + . = ALIGN(4); + PROVIDE(_data_lma = .); + } >FLASH AT>FLASH + + .data : + { + . = ALIGN(4); + PROVIDE(_data_vma = .); + *(.gnu.linkonce.r.*) + *(.data .data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + *(.sdata .sdata.*) + *(.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 = .); + __freertos_irq_stack_top = .; + } >RAM +} diff --git a/hw/bsp/ch583/system_ch583.c b/hw/bsp/ch583/system_ch583.c new file mode 100644 index 000000000..d3724f0ce --- /dev/null +++ b/hw/bsp/ch583/system_ch583.c @@ -0,0 +1,39 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "CH58x_common.h" +#include "system_ch583.h" + +uint32_t SystemCoreClock = FREQ_SYS; + +void SystemInit(void) { + SetSysClock(CLK_SOURCE_PLL_60MHz); + SystemCoreClock = GetSysClock(); +} + +void SystemCoreClockUpdate(void) { + SystemCoreClock = GetSysClock(); +} diff --git a/hw/bsp/ch583/system_ch583.h b/hw/bsp/ch583/system_ch583.h new file mode 100644 index 000000000..c174c9f5e --- /dev/null +++ b/hw/bsp/ch583/system_ch583.h @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 TinyUSB contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef SYSTEM_CH583_H_ +#define SYSTEM_CH583_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint32_t SystemCoreClock; // System Clock Frequency (Core Clock) + +extern void SystemInit(void); +extern void SystemCoreClockUpdate(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_CH583_H_ */ diff --git a/hw/bsp/ch583/wch-riscv.cfg b/hw/bsp/ch583/wch-riscv.cfg new file mode 100644 index 000000000..64d595d8e --- /dev/null +++ b/hw/bsp/ch583/wch-riscv.cfg @@ -0,0 +1,17 @@ +adapter driver wlinke +adapter speed 6000 +transport select sdi + +wlink_set_address 0x00000000 +set _CHIPNAME wch_riscv +sdi 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 0x8000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 + +echo "Ready for Remote Connections" diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index cc9837dcd..b5390a59d 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -659,6 +659,19 @@ #define TUP_DCD_EDPT_CLOSE_API #endif +#elif TU_CHECK_MCU(OPT_MCU_CH583) + // CH582/583 USBFS: older WCH USBFS IP with a single combined per-endpoint control register + // (like CH32V103), driven by the shared dcd_ch32_usbfs.c on USB0 (rhport 0). Device only: + // the shared hcd_ch32_usbfs.c is CH32V20x-specific and does not support CH58x, so host / + // USB2 (rhport 1) is not provided here. + #define TUP_USBIP_WCH_USBFS + + #ifndef CFG_TUD_WCH_USBIP_USBFS + #define CFG_TUD_WCH_USBIP_USBFS 1 + #endif + + #define TUP_DCD_ENDPOINT_MAX 8 + //--------------------------------------------------------------------+ // Analog Devices //--------------------------------------------------------------------+ diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h index 7ffdc6cef..415a015dc 100644 --- a/src/portable/wch/ch32_usbfs_reg.h +++ b/src/portable/wch/ch32_usbfs_reg.h @@ -130,6 +130,72 @@ #elif CFG_TUSB_MCU == OPT_MCU_CH32V307 #include <ch32v30x.h> #define USBHD_IRQn OTG_FS_IRQn +#elif CFG_TUSB_MCU == OPT_MCU_CH583 + #include "CH58x_common.h" + // CH582/583 USBFS device controller: same combined per-endpoint control register as + // CH32V103 (IN response bits[1:0], OUT response bits[3:2]) but a different register map - + // the EP control/length block sits lower (EP0_CTRL @ +0x22), EP5-7 are split out, EP4 + // shares EP0's DMA buffer, and EP5/6/7 mode bits live in one UEP567_MOD. The control/status + // block matches CH32. Two FS controllers exist (USB @ 0x40008000, USB2 @ 0x40008400); the + // device uses USB0. Full register map per CH583/582 datasheet Table 17-2; the parameterized + // EP_* macros below index off these named fields. + #define CH58X_USBFS_BASE 0x40008000u + // Per-endpoint register slots, 4-byte stride each; the EP_* macros index arrays of these. + typedef struct { + __IO uint16_t DMA; // R16_UEPn_DMA: endpoint n buffer start address + __IO uint16_t reserved; + } ch58x_ep_dma_t; + typedef struct { + __IO uint8_t T_LEN; // R8_UEPn_T_LEN (+0): transmit length + __IO uint8_t reserved0; + __IO uint8_t CTRL; // R8_UEPn_CTRL (+2): endpoint control + __IO uint8_t reserved1; + } ch58x_ep_ctrl_t; + typedef struct { + __IO uint8_t BASE_CTRL; // 0x00 R8_USB_CTRL + __IO uint8_t UDEV_CTRL; // 0x01 R8_UDEV_CTRL + __IO uint8_t INT_EN; // 0x02 R8_USB_INT_EN + __IO uint8_t DEV_ADDR; // 0x03 R8_USB_DEV_AD + __IO uint8_t Reserve0; // 0x04 + __IO uint8_t MIS_ST; // 0x05 R8_USB_MIS_ST + __IO uint8_t INT_FG; // 0x06 R8_USB_INT_FG + __IO uint8_t INT_ST; // 0x07 R8_USB_INT_ST + __IO uint8_t RX_LEN; // 0x08 R8_USB_RX_LEN (8-bit on CH58X) + __IO uint8_t Reserve1[3]; // 0x09..0x0B + __IO uint8_t UEP4_1_MOD; // 0x0C R8_UEP4_1_MOD + __IO uint8_t UEP2_3_MOD; // 0x0D R8_UEP2_3_MOD + __IO uint8_t UEP567_MOD; // 0x0E R8_UEP567_MOD + __IO uint8_t Reserve2; // 0x0F + ch58x_ep_dma_t EP_DMA_0_3[4]; // 0x10 EP0-3 DMA (EP4 has no DMA reg; it shares EP0's, index 0) + ch58x_ep_ctrl_t EP_CTRL_0_4[5]; // 0x20 EP0-4 length/control + __IO uint8_t Reserve3[0x54u - 0x34u]; // 0x34..0x53 + ch58x_ep_dma_t EP_DMA_5_7[3]; // 0x54 EP5-7 DMA + __IO uint8_t Reserve4[0x64u - 0x60u]; // 0x60..0x63 + ch58x_ep_ctrl_t EP_CTRL_5_7[3]; // 0x64 EP5-7 length/control + } USBOTG_FS_TypeDef; + #define USBOTG_FS ((USBOTG_FS_TypeDef *) CH58X_USBFS_BASE) + + // 4-byte slot stride + these block offsets pin every EP register to its datasheet address. + TU_VERIFY_STATIC(sizeof(ch58x_ep_dma_t) == 4, "CH58x EP DMA slot must be 4 bytes"); + TU_VERIFY_STATIC(sizeof(ch58x_ep_ctrl_t) == 4, "CH58x EP ctrl slot must be 4 bytes"); + TU_VERIFY_STATIC(offsetof(USBOTG_FS_TypeDef, EP_DMA_0_3) == 0x10, "CH58x EP_DMA_0_3 @0x10"); + TU_VERIFY_STATIC(offsetof(USBOTG_FS_TypeDef, EP_CTRL_0_4) == 0x20, "CH58x EP_CTRL_0_4 @0x20"); + TU_VERIFY_STATIC(offsetof(USBOTG_FS_TypeDef, EP_DMA_5_7) == 0x54, "CH58x EP_DMA_5_7 @0x54"); + TU_VERIFY_STATIC(offsetof(USBOTG_FS_TypeDef, EP_CTRL_5_7) == 0x64, "CH58x EP_CTRL_5_7 @0x64"); + + #define CH32_USBFS_EP_CTRL_COMBINED 1 + // 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 + // 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 + #define USBHD_IRQn USB_IRQn + #ifndef NVIC_EnableIRQ + #define NVIC_EnableIRQ(n) PFIC_EnableIRQ(n) + #define NVIC_DisableIRQ(n) PFIC_DisableIRQ(n) + #endif #endif #ifdef __GNUC__ @@ -167,9 +233,14 @@ #define USBFS_INT_FG_TOG_OK (1 << 6) #define USBFS_INT_FG_IS_NAK (1 << 7) +// MIS_ST: the SUSPEND interrupt fires on both suspend and resume; this bit (R8_USB_MIS_ST) is 1 +// while the bus is suspended and 0 once it has resumed, so it tells the two apart. +#define USBFS_MIS_ST_SUSPEND (1 << 2) + // INT_ST #define USBFS_INT_ST_MASK_UIS_ENDP(x) (((x) >> 0) & 0x0F) #define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03) +#define USBFS_INT_ST_TOG_OK (1 << 6) // received packet's data toggle matched expectation // UDEV_CTRL #define USBFS_UDEV_CTRL_PORT_EN (1 << 0) diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c index dae31da91..ece9cde07 100644 --- a/src/portable/wch/dcd_ch32_usbfs.c +++ b/src/portable/wch/dcd_ch32_usbfs.c @@ -35,17 +35,35 @@ /* private defines */ #define EP_MAX (8) - #define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep]) - #define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep]) - #define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep]) - #define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep]) + // Struct-based EP register access (uniform layout). CH58X has a different register map and + // defines EP_DMA/EP_TX_LEN/EP_CTRL itself in ch32_usbfs_reg.h. + #if CFG_TUSB_MCU == OPT_MCU_CH583 + // CH58X EP registers split into a low block (EP0-4) and a high block (EP5-7). Walk from each + // block's first slot by the 4-byte slot stride (pointer arithmetic off slot 0, so the unused + // ternary branch's index can't trip -Warray-bounds). EP4 has no DMA register of its own (it + // shares EP0's, slot 0) and is never written (see ep_shares_ep0_dma()). + #define EP_TX_LEN(ep) (*((ep) <= 4u ? &USBOTG_FS->EP_CTRL_0_4[0].T_LEN + (ep) * 4u \ + : &USBOTG_FS->EP_CTRL_5_7[0].T_LEN + ((ep) - 5u) * 4u)) + #define EP_CTRL(ep) (*((ep) <= 4u ? &USBOTG_FS->EP_CTRL_0_4[0].CTRL + (ep) * 4u \ + : &USBOTG_FS->EP_CTRL_5_7[0].CTRL + ((ep) - 5u) * 4u)) + #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)) + #else + #define EP_DMA(ep) ((&USBOTG_FS->UEP0_DMA)[ep]) + #define EP_TX_LEN(ep) ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep]) + #define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep]) + #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. #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 + #endif static inline uint8_t ep_tx_to_comb(uint8_t v) { uint8_t c = v & USBFS_EP_T_RES_MASK; // IN response: bits [1:0] in both encodings @@ -86,6 +104,17 @@ #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 +// 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 + /* private data */ struct usb_xfer { bool valid; @@ -99,28 +128,105 @@ static struct { bool ep0_tog; bool isochronous[EP_MAX]; 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 + // 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]; +#else TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64]; + // EP3 IN gets an enlarged buffer for full-speed isochronous (packets up to 1023 B). TU_ATTR_ALIGNED(4) struct { // OUT transfers >64 bytes will overwrite queued IN data! uint8_t out[64]; uint8_t in[1023]; uint8_t pad; } ep3_buffer; +#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; EP3 has an enlarged IN +// buffer for throughput. On CH58X, 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) { + 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 + } +} +#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 (ep == 3) { return (uint32_t) &data.ep3_buffer.out[0]; } + return (uint32_t) &data.buffer[ep][0]; +#endif +} + +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 (ep == 3) { return data.ep3_buffer.out; } + return data.buffer[ep][TUSB_DIR_OUT]; +#endif +} + +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 +#else + if (ep == 0) { return data.buffer[0][TUSB_DIR_OUT]; } // EP0 half-duplex: IN reuses OUT chunk + if (ep == 3) { return data.ep3_buffer.in; } + return data.buffer[ep][TUSB_DIR_IN]; +#endif +} + +// 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 +} + /* private helpers */ static void update_in(uint8_t rhport, uint8_t ep, bool force) { struct usb_xfer *xfer = &data.xfer[ep][TUSB_DIR_IN]; if (xfer->valid) { if (force || xfer->len) { size_t len = TU_MIN(xfer->max_size, xfer->len); - if (ep == 0) { - memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk - } else if (ep == 3) { - memcpy(data.ep3_buffer.in, xfer->buffer, len); - } else { - memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len); - } +#if CFG_TUSB_MCU == OPT_MCU_CH583 + // Every CH58x endpoint buffer is 64 bytes. Isochronous (which would push max_size up to 1023) + // is refused in dcd_edpt_iso_alloc(), but some classes (e.g. video) ignore that result, so cap + // the copy here to guarantee we never write past the buffer into a neighbouring endpoint's. + len = TU_MIN(len, 64u); +#endif + memcpy(ep_in_buf(ep), xfer->buffer, len); xfer->buffer += len; xfer->len -= len; xfer->processed_len += len; @@ -150,11 +256,10 @@ 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 (ep == 3) { - memcpy(xfer->buffer, data.ep3_buffer.out, len); - } else { - memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len); - } +#if CFG_TUSB_MCU == OPT_MCU_CH583 + len = TU_MIN(len, 64u); // cap to the 64-byte EP buffer (see update_in) +#endif + memcpy(xfer->buffer, ep_out_buf(ep), len); xfer->buffer += len; xfer->len -= len; xfer->processed_len += len; @@ -176,12 +281,11 @@ static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) { static void reset_ep_ctrls(void) { for (uint8_t ep = 1; ep < EP_MAX; ep++) { - EP_DMA(ep) = (uint32_t)&data.buffer[ep][0]; + if (!ep_shares_ep0_dma(ep)) { EP_DMA(ep) = ep_dma_addr(ep); } EP_TX_LEN(ep) = 0; - ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NYET); - ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET); + ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NYET); + ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET); } - EP_DMA(3) = (uint32_t)&data.ep3_buffer.out[0]; } /* public functions */ @@ -195,8 +299,8 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { USBOTG_FS->INT_FG = 0xFF; USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND; - // setup endpoint 0 - EP_DMA(0) = (uint32_t)&data.buffer[0][0]; + // setup endpoint 0 (also backs EP4's buffer on CH58X via the shared DMA region) + EP_DMA(0) = ep_dma_addr(0); EP_TX_LEN(0) = 0; ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK); ep_rx_ctrl_set(0, USBFS_EP_R_RES_ACK); @@ -204,8 +308,14 @@ 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 + // 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; +#else USBOTG_FS->UEP5_6_MOD = 0xCC; USBOTG_FS->UEP7_MOD = 0x0C; +#endif reset_ep_ctrls(); @@ -218,17 +328,32 @@ void dcd_int_handler(uint8_t rhport) { (void)rhport; uint8_t status = USBOTG_FS->INT_FG; if (status & USBFS_INT_FG_TRANSFER) { - uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST); - uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST); + uint8_t int_st = USBOTG_FS->INT_ST; + uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(int_st); + uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(int_st); uint16_t rx_len = USBOTG_FS->RX_LEN; 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. + if (ep != 0 && !(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). + 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) { EP_CTRL(ep) ^= USBFS_EPC_T_TOG; } +#endif update_in(rhport, ep, false); break; @@ -236,12 +361,17 @@ void dcd_int_handler(uint8_t rhport) { // setup clears stall ep_tx_ctrl_set(0, USBFS_EP_T_RES_NAK); data.ep0_tog = true; + // 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; + data.xfer[0][TUSB_DIR_IN].valid = false; - const tusb_control_request_t *setup = (const tusb_control_request_t *)&data.buffer[0][TUSB_DIR_OUT][0]; + 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); - dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true); + dcd_event_setup_received(rhport, ep0_out, true); break; } @@ -263,7 +393,14 @@ void dcd_int_handler(uint8_t rhport) { USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST; } else if (status & USBFS_INT_FG_SUSPEND) { +#if CFG_TUSB_MCU == OPT_MCU_CH583 + // CH58x raises this single interrupt for both suspend and resume; MIS_ST's suspend bit tells + // them apart (set while suspended, clear once resumed) so tud_resume_cb() actually fires. + dcd_event_t event = {.rhport = rhport, + .event_id = (USBOTG_FS->MIS_ST & USBFS_MIS_ST_SUSPEND) ? DCD_EVENT_SUSPEND : DCD_EVENT_RESUME}; +#else dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND}; +#endif dcd_event_handler(&event, true); USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND; } @@ -310,7 +447,12 @@ void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t *req (void)rhport; if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && request->bRequest == TUSB_REQ_SET_ADDRESS) { +#if CFG_TUSB_MCU == OPT_MCU_CH583 + // On CH58x R8_USB_DEV_AD bit 7 is a user general-purpose flag; only bits [6:0] are the address. + USBOTG_FS->DEV_ADDR = (uint8_t)((USBOTG_FS->DEV_ADDR & 0x80u) | (request->wValue & 0x7Fu)); +#else USBOTG_FS->DEV_ADDR = (uint8_t)request->wValue; +#endif } } @@ -323,10 +465,12 @@ bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep); if (ep != 0) { + // Opening clears the toggle to DATA0 (ep_*_ctrl_set writes the toggle bit clear since v has no + // R/T_TOG); with manual toggle EP_*_AUTO_TOG is 0 so the ISR owns subsequent toggling. if (dir == TUSB_DIR_OUT) { - ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK); + ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK); } else { - ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK); + ep_tx_ctrl_set(ep, EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK); } } return true; @@ -341,18 +485,29 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet (void)rhport; (void)ep_addr; (void)largest_packet_size; +#if CFG_TUSB_MCU == OPT_MCU_CH583 + // No isochronous support on CH58x: its 8-bit T_LEN caps a packet at 255B and the endpoints use + // plain 64-byte buffers, so accepting an iso max_size (up to 1023) would let update_in()/ + // update_out() run off the end of the buffer into neighbouring ones. Refuse it outright. + return false; +#else uint8_t ep = tu_edpt_number(ep_addr); uint8_t dir = tu_edpt_dir(ep_addr); data.isochronous[ep] = true; data.xfer[ep][dir].max_size = largest_packet_size; return true; +#endif } bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { (void)rhport; (void)desc_ep; +#if CFG_TUSB_MCU == OPT_MCU_CH583 + return false; // CH58x has no isochronous support (see dcd_edpt_iso_alloc) +#else return true; +#endif } bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { @@ -362,12 +517,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to uint8_t dir = tu_edpt_dir(ep_addr); struct usb_xfer *xfer = &data.xfer[ep][dir]; + // Keep the IRQ masked across the whole arming sequence: update_in()/ep_rx_set_response() do a + // 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); xfer->valid = true; xfer->buffer = buffer; xfer->len = total_bytes; xfer->processed_len = 0; - dcd_int_enable(rhport); if (dir == TUSB_DIR_IN) { update_in(rhport, ep, true); @@ -375,6 +532,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to uint8_t rx_res = data.isochronous[ep] ? USBFS_EP_R_RES_NYET : USBFS_EP_R_RES_ACK; ep_rx_set_response(ep, rx_res); } + dcd_int_enable(rhport); return true; } @@ -407,10 +565,11 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { ep_rx_ctrl_set(0, USBFS_EP_R_RES_ACK); } } else { + // clear-stall resets the toggle to DATA0 (USB spec); manual-toggle parts then re-sync via ISR if (dir == TUSB_DIR_OUT) { - ep_rx_ctrl_set(ep, USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK); + ep_rx_ctrl_set(ep, EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK); } else { - ep_tx_ctrl_set(ep, USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK); + 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 415e083c9..b5457fe8a 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -195,6 +195,8 @@ #define OPT_MCU_CH32F20X 2210 ///< WCH CH32F20x #define OPT_MCU_CH32V20X 2220 ///< WCH CH32V20X #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) // NXP LPC MCX #define OPT_MCU_MCXN9 2300 ///< NXP MCX N9 Series diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 055618e3c..07375c1ad 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -1485,9 +1485,13 @@ def test_device_hid_generic_inout(board): # device tests # note don't test 2 examples with cdc or 2 msc next to each other device_tests = [ + # Order matters: cdc_msc and cdc_msc_throughput share the same VID:PID (cafe:4003), so keep a + # differently-PID'd example (dfu, cafe:4000) between them. Boards whose CPU-reset does not drop + # D+ (e.g. WCH CH58x via openocd) only re-enumerate when the PID changes; back-to-back same-PID + # firmware would otherwise leave the host on the previous example's cached descriptors. 'device/cdc_dual_ports', - 'device/dfu', 'device/cdc_msc', + 'device/dfu', 'device/cdc_msc_throughput', 'device/audio_test_freertos', 'device/dfu_runtime', diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json index a534f2601..2ea910c4c 100644 --- a/test/hil/tinyusb.json +++ b/test/hil/tinyusb.json @@ -494,6 +494,21 @@ "uid": "BC4954081051", "args": "" } + }, + { + "name": "ch582m_evt", + "uid": "D443627B5450", + "toolchain": "riscv-gcc", + "tests": { + "device": true, + "host": false, + "dual": false + }, + "flasher": { + "name": "openocd_wch", + "uid": "7FD88F0604B5", + "args": "" + } } ], "boards-skip": [ diff --git a/tools/get_deps.py b/tools/get_deps.py index 60be5d89e..ebbf9b871 100755 --- a/tools/get_deps.py +++ b/tools/get_deps.py @@ -253,6 +253,9 @@ deps_optional = { 'hw/mcu/wch/ch32f20x': ['https://github.com/openwch/ch32f20x.git', '77c4095087e5ed2c548ec9058e655d0b8757663b', 'ch32f20x'], + 'hw/mcu/wch/ch583': ['https://github.com/openwch/ch583.git', + 'bd508ad7ceed48377619837051412a651952857f', + 'ch583'], 'hw/mcu/artery/at32f403a_407': ['https://github.com/ArteryTek/AT32F403A_407_Firmware_Library.git', 'f2cb360c3d28fada76b374308b8c4c61d37a090b', 'at32f403a_407'], |
