Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions hw/bsp/ch32v30x/ch32v30x_it.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
* SPDX-License-Identifier: Apache-2.0
*******************************************************************************/
#include "ch32v30x_it.h"
#include <stdio.h>
#include <stdint.h>

void NMI_Handler(void) __attribute__((naked));
void HardFault_Handler(void) __attribute__((naked));
Expand Down Expand Up @@ -39,8 +41,35 @@ void HardFault_Handler(void){
__asm volatile ("call HardFault_Handler_impl; mret");
}

// Direct USART1 output — no printf/stack/buffers, safe in a corrupted fault state.
static void fault_putc(char c)
{
volatile uint32_t* statr = (volatile uint32_t*) 0x40013800u; // USART1 STATR
volatile uint32_t* datar = (volatile uint32_t*) 0x40013804u; // USART1 DATAR
while (!(*statr & (1u << 7))) { } // wait TXE
*datar = (uint32_t) (uint8_t) c;
}
static void fault_puthex(uint32_t v)
{
for (int i = 7; i >= 0; i--) {
int n = (int) ((v >> (i * 4)) & 0xF);
fault_putc((char) (n < 10 ? '0' + n : 'A' + n - 10));
}
}

__attribute__((used)) void HardFault_Handler_impl(void)
{
uint32_t mcause = 0, mepc = 0, mtval = 0;
__asm volatile ("csrr %0, mcause" : "=r"(mcause));
__asm volatile ("csrr %0, mepc" : "=r"(mepc));
__asm volatile ("csrr %0, mtval" : "=r"(mtval));
const char* tag = "\nFAULT cause="; while (*tag) fault_putc(*tag++);
fault_puthex(mcause);
tag = " mepc="; while (*tag) fault_putc(*tag++);
fault_puthex(mepc);
tag = " mtval="; while (*tag) fault_putc(*tag++);
fault_puthex(mtval);
fault_putc('\n');
while (1)
{
}
Expand Down
42 changes: 28 additions & 14 deletions hw/bsp/ch32v30x/family.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,33 +61,39 @@ __attribute__((interrupt)) void USBHS_IRQHandler(void) {
#endif
}

__attribute__((interrupt)) void OTG_FS_IRQHandler(void) {
#if CFG_TUD_WCH_USBIP_USBFS
// The startup vector table names this vector USBFS_IRQHandler (OTG_FS_IRQn ==
// USBFS_IRQn == 83 on CH32V307). The OTG_FS register block is shared between the
// full-speed device (USBFSD) and host (USBFSH) views, so route the IRQ to
// whichever stack owns the USBFS controller in this build. (Previously this was
// named OTG_FS_IRQHandler, which is NOT the symbol the vector table references,
// so it was never installed — host-on-USBFS got no interrupts.)
__attribute__((interrupt)) void USBFS_IRQHandler(void) {
#if CFG_TUD_ENABLED && defined(CFG_TUD_WCH_USBIP_USBFS) && CFG_TUD_WCH_USBIP_USBFS
tud_int_handler(0);
#endif
#if CFG_TUH_ENABLED && defined(CFG_TUH_WCH_USBIP_USBFS) && CFG_TUH_WCH_USBIP_USBFS
tuh_int_handler(BOARD_TUH_RHPORT, true);
#endif
}

//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM
//--------------------------------------------------------------------+

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;
}

void board_init(void) {

/* Disable interrupts during init */
__disable_irq();

#if CFG_TUSB_OS == OPT_OS_NONE
SysTick_Config(SystemCoreClock / 1000);
// Free-running 64-bit SysTick (HCLK), no interrupt. board_millis() reads CNT
// directly (below), so the 1ms SysTick interrupt is neither used nor wanted.
// This stays compatible with an application HAL that reconfigures SysTick to the
// same free-running mode for its own time source.
SysTick->CTLR = 0;
SysTick->CNT = 0;
SysTick->CMP = 0xFFFFFFFFFFFFFFFFULL;
SysTick->CTLR = (1u << 0) | (1u << 2);
#endif

usart_printf_init(CFG_BOARD_UART_BAUDRATE);
Expand Down Expand Up @@ -145,7 +151,15 @@ __attribute__((interrupt)) void SysTick_Handler(void) {
}

uint32_t tusb_time_millis_api(void) {
return system_ticks;
// Derive ms from the free-running 64-bit SysTick CNT (HCLK rate) rather than
// the interrupt-driven `system_ticks`. An application that reconfigures SysTick
// to free-running mode WITHOUT the 1ms interrupt freezes `system_ticks` — and
// every tusb_time_delay_ms_api() that waits on board_millis() would then hang
// forever (breaking host enumeration delays).
// CNT keeps advancing in either SysTick mode, so this is always correct.
uint32_t per_ms = SystemCoreClock / 1000u;
if (per_ms == 0) per_ms = 1;
return (uint32_t) ((uint64_t) SysTick->CNT / per_ms);
}

#endif
Expand Down
1 change: 1 addition & 0 deletions hw/bsp/ch32v30x/family.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function(family_configure_example TARGET RTOS)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
${TOP}/src/portable/wch/dcd_ch32_usbhs.c
${TOP}/src/portable/wch/dcd_ch32_usbfs.c
${TOP}/src/portable/wch/hcd_ch32_usbfs.c
${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
)
target_include_directories(${TARGET} PUBLIC
Expand Down
1 change: 1 addition & 0 deletions hw/bsp/ch32v30x/family.mk
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ LDFLAGS += \
SRC_C += \
src/portable/wch/dcd_ch32_usbhs.c \
src/portable/wch/dcd_ch32_usbfs.c \
src/portable/wch/hcd_ch32_usbfs.c \
$(SDK_SRC_DIR)/Core/core_riscv.c \
$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \
$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \
Expand Down
29 changes: 23 additions & 6 deletions hw/bsp/ch32v30x/linker/ch32v30x.ld
Original file line number Diff line number Diff line change
Expand Up @@ -152,15 +152,32 @@ SECTIONS
PROVIDE( _ebss = .);
} >RAM AT>FLASH

._user_heap_stack :
/* Retained across NVIC_SystemReset (SRAM is not cleared by a soft reset, and
this is placed after _ebss so the startup .bss-zero loop skips it). Used by
the application to persist state across a reboot-driven mode switch. Heap
(_end) starts after this so it is not clobbered. */
.noinit (NOLOAD) :
{
. = ALIGN(8);
PROVIDE( end = . );
PROVIDE( _end = . );
. = . + __stack_size;
. = ALIGN(8);
. = ALIGN(4);
PROVIDE( _snoinit = . );
*(.noinit*)
. = ALIGN(4);
PROVIDE( _enoinit = . );
} >RAM

PROVIDE( _end = _enoinit );
PROVIDE( end = _enoinit );

.stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
{
PROVIDE( _heap_end = . );
. = ALIGN(4);
PROVIDE(_susrstack = . );
. = . + __stack_size;
PROVIDE( _eusrstack = .);
__freertos_irq_stack_top = .;
} >RAM

PROVIDE( _heap_end = ORIGIN(RAM) + LENGTH(RAM) - __stack_size );
PROVIDE( _susrstack = _heap_end );
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM) );
Expand Down
5 changes: 5 additions & 0 deletions src/common/tusb_mcu.h
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,11 @@
#define TUP_USBIP_WCH_USBHS
#define TUP_USBIP_WCH_USBFS

// USBFS can act as host (v307 has no USBHS host IP), same as v20x
#ifndef CFG_TUH_WCH_USBIP_USBFS
#define CFG_TUH_WCH_USBIP_USBFS 1
#endif

#if !defined(CFG_TUD_WCH_USBIP_USBFS)
#define CFG_TUD_WCH_USBIP_USBFS 0
#endif
Expand Down
13 changes: 12 additions & 1 deletion src/common/tusb_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,18 @@
} while(0)

#elif defined(__riscv) && !defined(ESP_PLATFORM)
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
// NOTE: unlike the ARM path above, a plain `ebreak` does NOT check whether a
// debugger is attached. On WCH QingKe cores (CH32V307) the debug module is
// left configured by the WCH-LinkE after flashing, so an `ebreak` from a
// failed TU_ASSERT/TU_VERIFY (e.g. a transient error during host enumeration)
// RESETS the chip (SFT reset, never reaching the trap handler) instead of
// returning false. That turns recoverable enumeration hiccups into reboot
// loops. Make it a no-op on CH32 so TU_ASSERT just returns its error value.
#if defined(CFG_TUSB_MCU) && (CFG_TUSB_MCU == OPT_MCU_CH32V307 || CFG_TUSB_MCU == OPT_MCU_CH32V20X || CFG_TUSB_MCU == OPT_MCU_CH32F20X)
#define TU_BREAKPOINT() do {} while (0)
#else
#define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
#endif

#elif defined(_mips)
#define TU_BREAKPOINT() do { __asm("sdbbp 0"); } while (0)
Expand Down
124 changes: 123 additions & 1 deletion src/portable/wch/ch32_usbfs_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,22 @@
#define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400)
#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
#include <ch32v20x.h>
#include <ch32v20x_usb.h>
#elif CFG_TUSB_MCU == OPT_MCU_CH32V307
#include <ch32v30x.h>
#include <ch32v30x_usb.h>
#define USBHD_IRQn OTG_FS_IRQn
#endif

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

// we only speak up to full-speed here, i.e. 64 bytes per packet
#define MAX_PACKET_SIZE 64



// CTRL
#define USBFS_CTRL_DMA_EN (1 << 0)
#define USBFS_CTRL_CLR_ALL (1 << 1)
Expand Down Expand Up @@ -172,4 +179,119 @@
#define PID_IN 2
#define PID_SETUP 3

#endif // USB_CH32_USBFS_REG_H
// USB host defines taken from ch32v20x_usb.h
#define USBFS_UH_EP_TX_EN 0x40
#define USBFS_UH_EP_RX_EN 0x08

#define USBFS_UIF_DETECT 0x01
#define USBFS_UMS_DEV_ATTACH 0x01

#define USBFS_UDA_GP_BIT 0x80
#define USBFS_USB_ADDR_MASK 0x7F

#define USBFS_UH_LOW_SPEED 0x04
#define USBFS_UH_PRE_PID_EN 0x0400

#define USBFS_UH_BUS_RESET 0x02
#define USBFS_UH_PORT_EN 0x01

#define USBFS_UMS_DM_LEVEL 0x02
#define USB_LOW_SPEED 0x00
#define USB_FULL_SPEED 0x01
#define USBFS_UC_LOW_SPEED 0x40

#define USBFS_UH_SOF_EN 0x0004

/* USB PID */
#ifndef USB_PID_SETUP
#define USB_PID_NULL 0x00
#define USB_PID_SOF 0x05
#define USB_PID_SETUP 0x0D
#define USB_PID_IN 0x09
#define USB_PID_OUT 0x01
#define USB_PID_NYET 0x06
#define USB_PID_ACK 0x02
#define USB_PID_NAK 0x0A
#define USB_PID_STALL 0x0E
#define USB_PID_DATA0 0x03
#define USB_PID_DATA1 0x0B
#define USB_PID_PRE 0x0C
#endif

#define USBFS_UIF_HST_SOF 0x08
#define USBFS_UIF_TRANSFER 0x02

/* R8_USB_MIS_ST */
#define USBFS_UMS_SOF_PRES 0x80
#define USBFS_UMS_SOF_ACT 0x40
#define USBFS_UMS_SIE_FREE 0x20
#define USBFS_UMS_R_FIFO_RDY 0x10
#define USBFS_UMS_BUS_RESET 0x08
#define USBFS_UMS_SUSPEND 0x04
#define USBFS_UMS_DM_LEVEL 0x02
#define USBFS_UMS_DEV_ATTACH 0x01

#define USBFS_UIS_IS_NAK 0x80 // RO, indicate current USB transfer is NAK received for USB device mode
#define USBFS_UIS_TOG_OK 0x40 // RO, indicate current USB transfer toggle is OK
#define USBFS_UIS_TOKEN_MASK 0x30// RO, bit mask of current token PID code received for USB device mode
#define USBFS_UIS_TOKEN_OUT 0x00
#define USBFS_UIS_TOKEN_SOF 0x10
#define USBFS_UIS_TOKEN_IN 0x20
#define USBFS_UIS_TOKEN_SETUP 0x30

#define USBFS_UH_ENDP_MASK 0x0F
#define USBFS_UIS_H_RES_MASK 0x0F// RO, bit mask of current transfer handshake response for USB host mode:
#define USBFS_UH_TOKEN_MASK 0xF0


/* R8_UHOST_CTRL */
#define USBFS_UH_PD_DIS 0x80 // disable USB UDP/UDM pulldown resistance: 0=enable pulldown, 1=disable
#define USBFS_UH_DP_PIN 0x20 // ReadOnly: indicate current UDP pin level
#define USBFS_UH_DM_PIN 0x10 // ReadOnly: indicate current UDM pin level
#define USBFS_UH_LOW_SPEED 0x04// enable USB port low speed: 0=full speed, 1=low speed
#define USBFS_UH_BUS_RESET 0x02// control USB bus reset: 0=normal, 1=force bus reset
#define USBFS_UH_PORT_EN 0x01 // enable USB port: 0=disable, 1=enable port, automatic disabled if USB device detached

/* R32_UH_EP_MOD */
#define USBFS_UH_EP_TX_EN 0x40 // enable USB host OUT endpoint transmittal
#define USBFS_UH_EP_TBUF_MOD 0x10// buffer mode of USB host OUT endpoint
// bUH_EP_TX_EN & bUH_EP_TBUF_MOD: USB host OUT endpoint buffer mode, buffer start address is UH_TX_DMA
// 0 x: disable endpoint and disable buffer
// 1 0: 64 bytes buffer for transmittal (OUT endpoint)
// 1 1: dual 64 bytes buffer by toggle bit bUH_T_TOG selection for transmittal (OUT endpoint), total=128bytes
#define USBFS_UH_EP_RX_EN 0x08 // enable USB host IN endpoint receiving
#define USBFS_UH_EP_RBUF_MOD 0x01// buffer mode of USB host IN endpoint
// bUH_EP_RX_EN & bUH_EP_RBUF_MOD: USB host IN endpoint buffer mode, buffer start address is UH_RX_DMA
// 0 x: disable endpoint and disable buffer
// 1 0: 64 bytes buffer for receiving (IN endpoint)
// 1 1: dual 64 bytes buffer by toggle bit bUH_R_TOG selection for receiving (IN endpoint), total=128bytes

/* R16_UH_SETUP */
#define USBFS_UH_PRE_PID_EN 0x0400// USB host PRE PID enable for low speed device via hub
#define USBFS_UH_SOF_EN 0x0004 // USB host automatic SOF enable

/* R8_UH_EP_PID */
#define USBFS_UH_TOKEN_MASK 0xF0// bit mask of token PID for USB host transfer
#define USBFS_UH_ENDP_MASK 0x0F // bit mask of endpoint number for USB host transfer

/* R8_UH_RX_CTRL */
#define USBFS_UH_R_AUTO_TOG 0x08// enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle
#define USBFS_UH_R_TOG 0x04 // expected data toggle flag of host receiving (IN): 0=DATA0, 1=DATA1
#define USBFS_UH_R_RES 0x01 // prepared handshake response type for host receiving (IN): 0=ACK (ready), 1=no response, time out to device, for isochronous transactions

/* R8_UH_TX_CTRL */
#define USBFS_UH_T_AUTO_TOG 0x08// enable automatic toggle after successful transfer completion: 0=manual toggle, 1=automatic toggle
#define USBFS_UH_T_TOG 0x04 // prepared data toggle flag of host transmittal (SETUP/OUT): 0=DATA0, 1=DATA1
#define USBFS_UH_T_RES 0x01 // expected handshake response type for host transmittal (SETUP/OUT): 0=ACK (ready), 1=no response, time out from device, for isochronous transactions

/* R8_USB_MIS_ST */
#define USBFS_UMS_SOF_PRES 0x80
#define USBFS_UMS_SOF_ACT 0x40
#define USBFS_UMS_SIE_FREE 0x20
#define USBFS_UMS_R_FIFO_RDY 0x10
#define USBFS_UMS_BUS_RESET 0x08
#define USBFS_UMS_SUSPEND 0x04
#define USBFS_UMS_DM_LEVEL 0x02
#define USBFS_UMS_DEV_ATTACH 0x01

#endif// USB_CH32_USBFS_REG_H
Loading
Loading