From 7fa50c3f8efdcce10f9519aa520014bdfdc09d2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:22 +0200 Subject: [PATCH 01/10] Add declaration macro to fifo --- src/wch-ch56x-lib/memory/fifo.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wch-ch56x-lib/memory/fifo.h b/src/wch-ch56x-lib/memory/fifo.h index f1a7c15..7b74fc7 100644 --- a/src/wch-ch56x-lib/memory/fifo.h +++ b/src/wch-ch56x-lib/memory/fifo.h @@ -78,6 +78,10 @@ typedef struct hydra_fifo_t volatile uint16_t wr_idx; } hydra_fifo_t; +#define HYDRA_FIFO_DECLR(_name, _type, _size) \ + extern uint8_t _name##_buffer[(_size + 1) * sizeof(_type)]; \ + extern hydra_fifo_t _name + #define HYDRA_FIFO_DEF(_name, _type, _size) \ uint8_t _name##_buffer[(_size + 1) * sizeof(_type)]; \ hydra_fifo_t _name = { .buffer = _name##_buffer, \ From afd66334efb73169903341d2746250c524ce7fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:24 +0200 Subject: [PATCH 02/10] Add NAK callback support --- src/wch-ch56x-lib/USBDevice/usb20.c | 21 +++++++++++++++++++++ src/wch-ch56x-lib/USBDevice/usb20.h | 8 +++++++- src/wch-ch56x-lib/USBDevice/usb_device.c | 8 ++++++-- src/wch-ch56x-lib/USBDevice/usb_endpoints.h | 3 +++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/wch-ch56x-lib/USBDevice/usb20.c b/src/wch-ch56x-lib/USBDevice/usb20.c index 79aa35c..e8b91d5 100644 --- a/src/wch-ch56x-lib/USBDevice/usb20.c +++ b/src/wch-ch56x-lib/USBDevice/usb20.c @@ -704,6 +704,18 @@ void usb2_endp_tx_set_state_callback(uint8_t endp_num) *TX_CTRL = (*TX_CTRL & ~RB_UEP_TRES_MASK) | endp->state; } +void usb2_enable_nak(bool enable) +{ + if (enable) + { + R8_USB_INT_EN |= RB_USB_IE_DEV_NAK; + } + else + { + R8_USB_INT_EN &= ~RB_USB_IE_DEV_NAK; + } +} + void usb2_endp_tx_ready(uint8_t endp_num, uint16_t size) { volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.tx[endp_num]; @@ -737,6 +749,14 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBHS_IRQHandler(void) uint8_t usb_pid = (R8_USB_INT_ST & RB_DEV_TOKEN_MASK) >> 4; LOG_IF(LOG_LEVEL_TRACE, LOG_ID_TRACE, "USBHS_IRQHandler-start\r\n"); + + if (!(R8_USB_INT_FG & RB_USB_IF_SETUOACT) && (R8_USB_INT_ST & RB_USB_ST_NAK)) + { + usb2_backend_current_device->endpoints.nak_callback(usb_dev_endp); + R8_USB_INT_FG = R8_USB_INT_FG; + return; + } + if (R8_USB_INT_FG & RB_USB_IF_SETUOACT && usb2_backend_current_device->state != POWERED) { usb_setup_req = *(USB_SETUP*)usb2_backend_current_device->endpoints.rx[0].buffer; @@ -800,6 +820,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBHS_IRQHandler(void) case 5: case 6: case 7: + if (usb_pid == PID_IN) { usb2_in_transfer_handler(usb_dev_endp); diff --git a/src/wch-ch56x-lib/USBDevice/usb20.h b/src/wch-ch56x-lib/USBDevice/usb20.h index fd7069f..1db6ef3 100644 --- a/src/wch-ch56x-lib/USBDevice/usb20.h +++ b/src/wch-ch56x-lib/USBDevice/usb20.h @@ -124,7 +124,13 @@ void usb2_endp_tx_set_state_callback(uint8_t endp_num); */ void usb2_endp_rx_set_state_callback(uint8_t endp_num); -void USBHS_IRQHandler(void); +/** + * @brief Enable NAK status + * @param endp_num + */ +void usb2_enable_nak(bool enable); + +__attribute__((interrupt("WCH-Interrupt-fast"))) void USBHS_IRQHandler(void); #ifdef __cplusplus } diff --git a/src/wch-ch56x-lib/USBDevice/usb_device.c b/src/wch-ch56x-lib/USBDevice/usb_device.c index 8b6bf26..7d16a27 100644 --- a/src/wch-ch56x-lib/USBDevice/usb_device.c +++ b/src/wch-ch56x-lib/USBDevice/usb_device.c @@ -36,6 +36,8 @@ uint8_t _default_endp_rx_callback(uint8_t* const ptr, uint16_t size) { return ENDP_STATE_STALL; } +void _default_nak_callback(uint8_t ep_num); +void _default_nak_callback(uint8_t ep_num) {} /** * Setting up default handlers here : this prevents the device from crashing and avoid an unnecessary check for handlers that are user-defined. @@ -62,7 +64,8 @@ usb_device_t usb_device_0 = { _default_endp_rx_callback, _default_endp_rx_callback, _default_endp_rx_callback, - _default_endp_rx_callback } } + _default_endp_rx_callback }, + .nak_callback = _default_nak_callback } }; usb_device_t usb_device_1 = { @@ -87,7 +90,8 @@ usb_device_t usb_device_1 = { _default_endp_rx_callback, _default_endp_rx_callback, _default_endp_rx_callback, - _default_endp_rx_callback } } + _default_endp_rx_callback }, + .nak_callback = _default_nak_callback } }; void usb_device_set_addr(usb_device_t* usb_device, uint8_t addr) diff --git a/src/wch-ch56x-lib/USBDevice/usb_endpoints.h b/src/wch-ch56x-lib/USBDevice/usb_endpoints.h index b3abd56..71263e2 100644 --- a/src/wch-ch56x-lib/USBDevice/usb_endpoints.h +++ b/src/wch-ch56x-lib/USBDevice/usb_endpoints.h @@ -44,6 +44,7 @@ typedef struct usb_endpoints_t { volatile USB_ENDPOINT tx[8]; volatile USB_ENDPOINT rx[8]; + /** * @brief Process requests unhandled by the backend. Must return 0xffff if it * does not handle the request. @@ -79,6 +80,8 @@ typedef struct usb_endpoints_t */ uint8_t (*rx_callback[8])(uint8_t* const ptr, uint16_t size); + void (*nak_callback)(uint8_t ep_num); + } usb_endpoints_t; typedef struct usb2_endpoints_backend_handled_t From c3dd124f6d7ec7683854aec1522bcb60b816e798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:24 +0200 Subject: [PATCH 03/10] Handle 0-length vendor control requests --- src/wch-ch56x-lib/USBDevice/usb30.c | 7 +++---- src/wch-ch56x-lib/USBDevice/usb30.h | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wch-ch56x-lib/USBDevice/usb30.c b/src/wch-ch56x-lib/USBDevice/usb30.c index 355abce..607b718 100644 --- a/src/wch-ch56x-lib/USBDevice/usb30.c +++ b/src/wch-ch56x-lib/USBDevice/usb30.c @@ -902,7 +902,6 @@ void usb3_endp_tx_ready(uint8_t endp_num, uint16_t size) * * @return None */ -__attribute__((interrupt("WCH-Interrupt-fast"))) void LINK_IRQHandler(void); __attribute__((interrupt("WCH-Interrupt-fast"))) void LINK_IRQHandler(void) { if (USBSS->LINK_INT_FLAG & LINK_Ux_EXIT_FLAG) // device enter U2 @@ -1091,7 +1090,6 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void LINK_IRQHandler(void) * * @return None */ -__attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void); __attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void) { static uint32_t count = 0; @@ -1212,6 +1210,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void) req_len = usb3_backend_current_device->endpoints.endp0_user_handled_control_request( UsbSetupBuf, &buffer); + SetupLen = req_len; // handle IN non standard requests if (req_len != USB_DESCR_UNSUPPORTED && endp_dir) { @@ -1245,7 +1244,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void) USBSS->UEP0_TX_CTRL = 0x8010000; // USB30_IN_set(0, DISABLE, STALL, 1, 0); return; } - if (req_len != 0) + else { if (0x200 < req_len) { @@ -1265,7 +1264,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void) USBSS->UEP0_RX_CTRL = 0x8010000; // USB30_IN_set(0, DISABLE, STALL, 1, 0); return; } - if (req_len != 0) + else { if (0x200 < req_len) { diff --git a/src/wch-ch56x-lib/USBDevice/usb30.h b/src/wch-ch56x-lib/USBDevice/usb30.h index 97b4ae1..389757c 100644 --- a/src/wch-ch56x-lib/USBDevice/usb30.h +++ b/src/wch-ch56x-lib/USBDevice/usb30.h @@ -184,6 +184,8 @@ void usb30_itp_callback(uint32_t ITPCounter); */ void usb3_endp_tx_ready(uint8_t endp_num, uint16_t size); +__attribute__((interrupt("WCH-Interrupt-fast"))) void LINK_IRQHandler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void USBSS_IRQHandler(void); #ifdef __cplusplus } #endif From bf9d59378bb6fc0c8727ebea628768e1df8a3484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:25 +0200 Subject: [PATCH 04/10] Inline most fifo functions --- src/wch-ch56x-lib/memory/fifo.h | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/wch-ch56x-lib/memory/fifo.h b/src/wch-ch56x-lib/memory/fifo.h index 7b74fc7..c64d400 100644 --- a/src/wch-ch56x-lib/memory/fifo.h +++ b/src/wch-ch56x-lib/memory/fifo.h @@ -130,11 +130,10 @@ __attribute__((always_inline)) static inline uint16_t _fifo_read_n(hydra_fifo_t* fifo, void* buffer, uint16_t n, uint16_t rd_idx, uint16_t wr_idx) { - LOG_IF_LEVEL( - LOG_LEVEL_TRACE, - "_fifo_read_n rd_idx=%d wr_idx=%d n=%d free_space=%d count%d \r\n", - rd_idx, wr_idx, n, _fifo_get_free_space(rd_idx, wr_idx, fifo->size), - _fifo_get_count(rd_idx, wr_idx, fifo->size)); + LOG_IF_LEVEL(LOG_LEVEL_TRACE, + "_fifo_read_n rd_idx=%d wr_idx=%d n=%d free_space=%d count%d \r\n", + rd_idx, wr_idx, n, _fifo_get_free_space(rd_idx, wr_idx, fifo->size), + _fifo_get_count(rd_idx, wr_idx, fifo->size)); if (rd_idx == wr_idx || (n > _fifo_get_count(rd_idx, wr_idx, fifo->size))) return 0; @@ -176,11 +175,10 @@ __attribute__((always_inline)) static inline uint16_t _fifo_write_n(hydra_fifo_t* fifo, void* buffer, uint16_t n, uint16_t rd_idx, uint16_t wr_idx) { - LOG_IF_LEVEL( - LOG_LEVEL_TRACE, - "_fifo_write_n rd_idx=%d wr_idx=%d n=%d free_space=%d count=%d \r\n", - rd_idx, wr_idx, n, _fifo_get_free_space(rd_idx, wr_idx, fifo->size), - _fifo_get_count(rd_idx, wr_idx, fifo->size)); + LOG_IF_LEVEL(LOG_LEVEL_TRACE, + "_fifo_write_n rd_idx=%d wr_idx=%d n=%d free_space=%d count=%d \r\n", + rd_idx, wr_idx, n, _fifo_get_free_space(rd_idx, wr_idx, fifo->size), + _fifo_get_count(rd_idx, wr_idx, fifo->size)); uint16_t free_space = _fifo_get_free_space(rd_idx, wr_idx, fifo->size); if (free_space < n) @@ -270,6 +268,7 @@ fifo_read(hydra_fifo_t* fifo, void* buffer) uint16_t count_read = _fifo_read_n(fifo, buffer, _fifo_get_count(rd_idx, wr_idx, fifo->size), rd_idx, wr_idx); + _fifo_advance_read(fifo, rd_idx, count_read); hydra_fifo_disable_interrupt(false); return count_read; } From 199e0c5de9844c28fa5a53db70b8f4c94c9ade46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:25 +0200 Subject: [PATCH 05/10] Add fifo test firmware, testing reentrance after after interrupt --- tests/CMakeLists.txt | 1 + tests/test_firmware_fifo/.gitignore | 57 ++++++++ tests/test_firmware_fifo/.ld | 1 + tests/test_firmware_fifo/CMakeLists.txt | 101 ++++++++++++++ tests/test_firmware_fifo/User/main.c | 178 ++++++++++++++++++++++++ tests/test_firmware_fifo/format.sh | 3 + 6 files changed, 341 insertions(+) create mode 100644 tests/test_firmware_fifo/.gitignore create mode 100644 tests/test_firmware_fifo/.ld create mode 100644 tests/test_firmware_fifo/CMakeLists.txt create mode 100644 tests/test_firmware_fifo/User/main.c create mode 100644 tests/test_firmware_fifo/format.sh diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4403433..9c02f58 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(test_firmware_serdes) add_subdirectory(test_firmware_unittests) add_subdirectory(test_firmware_usb_loopback) add_subdirectory(test_firmware_usb_speedtest) +add_subdirectory(test_firmware_fifo) add_subdirectory(test_firmware_usb_loopback_separate_usb_stacks) diff --git a/tests/test_firmware_fifo/.gitignore b/tests/test_firmware_fifo/.gitignore new file mode 100644 index 0000000..0470403 --- /dev/null +++ b/tests/test_firmware_fifo/.gitignore @@ -0,0 +1,57 @@ +# Prerequisites +*.d + +# astyle generated +*.*.orig + +# Object files +*.o +*.ko +*.obj +*.elf +*.bin +*.lst + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/tests/test_firmware_fifo/.ld b/tests/test_firmware_fifo/.ld new file mode 100644 index 0000000..12f10dd --- /dev/null +++ b/tests/test_firmware_fifo/.ld @@ -0,0 +1 @@ +/* bvernoux 18June2022 => Changed SECTION ".DMADATA :" to ".DMADATA (NOLOAD) :" => Added in section ".DMADATA" => *(.DMADATA*) => To have a correct _dmadata_end (as before _dmadata_start was always equal to _dmadata_end) */ ENTRY( _start ) __stack_size = 2048; PROVIDE( _stack_size = __stack_size ); MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16K RAMX (xrw) : ORIGIN = 0x20020000, LENGTH = 96K } 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*) *(.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 : { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin.o(.ctors)) KEEP (*crtbegin?.o(.ctors)) /* We don't want to include the .ctor section from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ 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 = . ); .DMADATA (NOLOAD) : { . = ALIGN(16); PROVIDE( _dmadata_start = .); *(.dmadata*) *(.dmadata.*) *(.DMADATA*) . = ALIGN(16); PROVIDE( _dmadata_end = .); } >RAMX AT>FLASH /**/ .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : { . = ALIGN(4); PROVIDE(_susrstack = . ); . = . + __stack_size; PROVIDE( _eusrstack = .); } >RAM } \ No newline at end of file diff --git a/tests/test_firmware_fifo/CMakeLists.txt b/tests/test_firmware_fifo/CMakeLists.txt new file mode 100644 index 0000000..4179763 --- /dev/null +++ b/tests/test_firmware_fifo/CMakeLists.txt @@ -0,0 +1,101 @@ +project(test_firmware_fifo LANGUAGES C) +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +add_executable(${PROJECT_NAME}) + +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/User/main.c + ) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/User) + +##### Define program options + +#### wch-ch56x-lib options + +target_compile_definitions(wch-ch56x-lib-scheduled INTERFACE POOL_BLOCK_SIZE=512 POOL_BLOCK_NUM=40 INTERRUPT_QUEUE_SIZE=20) + +#### logging options + +# set(LOG_OUTPUT "printf") +# set(LOG_LEVEL 1) +# set(LOG_FILTER_IDS "1") + +if (DEFINED LOG_OUTPUT) + if (${LOG_OUTPUT} STREQUAL "buffer") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_BUFFER=1) + endif() + if (${LOG_OUTPUT} STREQUAL "serdes") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_SERDES=1) + endif() + if (${LOG_OUTPUT} STREQUAL "printf") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_PRINTF=1) + endif() +endif() + +if (DEFINED LOG_LEVEL) + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_LEVEL=${LOG_LEVEL}) +endif() + +if (DEFINED LOG_FILTER_IDS) + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_FILTER_IDS=${LOG_FILTER_IDS}) +endif() + +if (DEFINED STATIC_ANALYSIS) + target_compile_options(${PROJECT_NAME} PRIVATE -fanalyzer) +endif() + +##### Compilation and linkage options + +target_compile_options(${PROJECT_NAME} PRIVATE + -Werror -Wno-comment -pedantic -Wall -Wno-error=unused-parameter + -Wbad-function-cast -Wredundant-decls -Wmissing-prototypes -Wchar-subscripts -Wshadow -Wundef -Wwrite-strings -Wunused -Wuninitialized -Wpointer-arith -Winline -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wmissing-declarations -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits -Wcast-align -Wswitch-enum + -Wextra -Wclobbered -Wcast-function-type -Wimplicit-fallthrough=3 -Wmissing-parameter-type -Wold-style-declaration -Woverride-init -Wshift-negative-value -Wunused-but-set-parameter +) + +if (DEFINED EXTRACFLAGS) +target_compile_options(${PROJECT_NAME} PRIVATE + -Wunused-parameter -Wno-error=unused-parameter + -Wsign-compare -Wno-error=sign-compare + -Wconversion -Wno-error=conversion -Wno-error=sign-conversion -Wno-error=float-conversion +) +endif() + +if (${RISCV_GCC_TOOLCHAIN_PREFIX} STREQUAL "riscv-none-embed-gcc") + message("Using riscv-none-embed-gcc") + target_compile_options(${PROJECT_NAME} PRIVATE -march=rv32imac) +elseif(${RISCV_GCC_TOOLCHAIN_PREFIX} STREQUAL "riscv-none-elf-gcc") + message("Using riscv-none-elf-gcc") + target_compile_options(${PROJECT_NAME} PRIVATE -march=rv32imac_zicsr) +else() + message("Toolchain not found") +endif() + +target_compile_options(${PROJECT_NAME} PRIVATE -std=gnu99 -MMD -MP -mabi=ilp32 -msmall-data-limit=8 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections) +target_compile_options(${PROJECT_NAME} PRIVATE $<$:-Og> $<$:-Oz>) +target_link_options(${PROJECT_NAME} PRIVATE -T "${CMAKE_CURRENT_LIST_DIR}/.ld" -nostartfiles LINKER:--gc-sections LINKER:--print-memory-usage -Wl,-Map,${PROJECT_NAME}.map --specs=nano.specs --specs=nosys.specs) +target_link_libraries(${PROJECT_NAME} wch-ch56x-lib-scheduled) + +##### Generate additional targets + +add_custom_target(${PROJECT_NAME}.bin ALL DEPENDS ${PROJECT_NAME}.elf) +add_custom_target(${PROJECT_NAME}.hex ALL DEPENDS ${PROJECT_NAME}.elf) +add_custom_target(${PROJECT_NAME}.lst ALL DEPENDS ${PROJECT_NAME}.elf) + +add_custom_command(TARGET ${PROJECT_NAME}.bin + COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf + ${PROJECT_NAME}.bin) + +add_custom_command(TARGET ${PROJECT_NAME}.hex + COMMAND ${CMAKE_OBJCOPY} -O ihex ${PROJECT_NAME}.elf + ${PROJECT_NAME}.hex) + +add_custom_command(TARGET ${PROJECT_NAME}.lst + COMMAND ${CMAKE_OBJDUMP} --source --all-headers --demangle --line-numbers --wide ${PROJECT_NAME}.elf > ${PROJECT_NAME}.lst) + +##### Export generated files + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.hex DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.elf DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.lst DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.map DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) diff --git a/tests/test_firmware_fifo/User/main.c b/tests/test_firmware_fifo/User/main.c new file mode 100644 index 0000000..11110b6 --- /dev/null +++ b/tests/test_firmware_fifo/User/main.c @@ -0,0 +1,178 @@ +/********************************** (C) COPYRIGHT ******************************* +Copyright (c) 2023 Quarkslab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*******************************************************************************/ + +// Disable warnings in bsp arising from -pedantic -Wall -Wconversion +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#include "CH56x_common.h" +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop + +#include "wch-ch56x-lib/logging/logging.h" +#include "wch-ch56x-lib/memory/fifo.h" + +#undef FREQ_SYS +/* System clock / MCU frequency in Hz (lowest possible speed 15MHz) */ +#define FREQ_SYS (120000000) + +#define FIFO_SIZE 1000 + +#define MAX_COUNT 1000 + +#define TMR_CLOCK_CYCLES 100000 + +#define RATIO 10 + +HYDRA_FIFO_DEF(fifo, uint16_t, FIFO_SIZE); + +volatile uint16_t count = 0; +/********************************************************************* + * @fn main + * + * @brief Main program. + * + * @return none + */ +int main() +{ + bsp_gpio_init(); + + bsp_init(FREQ_SYS); + + LOG_INIT(FREQ_SYS); + + LOG("FIFO stress test\r\n"); + + TMR0_TimerInit(TMR_CLOCK_CYCLES); + R8_TMR0_INTER_EN = RB_TMR_IE_CYC_END; + PFIC_EnableIRQ(TMR0_IRQn); + + uint16_t buffer[FIFO_SIZE]; + while (1) + { + uint16_t read = fifo_read(&fifo, buffer); + + if (read > 0) + { + LOG("Total read %d \r\n", read); + for (uint16_t i = 0; i < read; ++i) + { + LOG("Read %d\r\n", buffer[i]); + if (buffer[i] >= MAX_COUNT) + { + goto end; + } + } + } + // bsp_wait_nb_cycles(TMR_CLOCK_CYCLES / RATIO); + } +end: + return 0; +} + +/******************************************************************************* + * @fn TMR0_IRQHandler + * @return None + */ +__attribute__((interrupt("WCH-Interrupt-fast"))) void TMR0_IRQHandler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void TMR0_IRQHandler(void) +{ + R8_TMR0_INT_FLAG = RB_TMR_IF_CYC_END; + LOG("clock \r\n"); + if (count < MAX_COUNT) + { + ++count; + uint16_t temp = count; + fifo_write(&fifo, &temp, 1); + LOG("writing %d \r\n", count); + TMR0_TimerInit(TMR_CLOCK_CYCLES); + } + else + { + R8_TMR0_INTER_EN = 0; + PFIC_DisableIRQ(TMR0_IRQn); + R8_TMR0_CTRL_MOD = RB_TMR_ALL_CLEAR; + } + return; +} + +__attribute__((interrupt("WCH-Interrupt-fast"))) void WDOG_IRQHandler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void WDOG_IRQHandler(void) +{ + LOG_DUMP(); + + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, + "WDOG_IRQHandler\r\n" + " SP=0x%08X\r\n" + " MIE=0x%08X\r\n" + " MSTATUS=0x%08X\r\n" + " MCAUSE=0x%08X\r\n" + " MVENDORID=0x%08X\r\n" + " MARCHID=0x%08X\r\n" + " MISA=0x%08X\r\n" + " MIMPID=0x%08X\r\n" + " MHARTID=0x%08X\r\n" + " MEPC=0x%08X\r\n" + " MSCRATCH=0x%08X\r\n" + " MTVEC=0x%08X\r\n", + __get_SP(), __get_MIE(), __get_MSTATUS(), __get_MCAUSE(), + __get_MVENDORID(), __get_MARCHID(), __get_MISA(), __get_MIMPID(), + __get_MHARTID(), __get_MEPC(), __get_MSCRATCH(), __get_MTVEC()); + + LOG_DUMP(); + + bsp_wait_ms_delay(100000000); +} + +/********************************************************************* + * @fn HardFault_Handler + * + * @brief Example of basic HardFault Handler called if an exception occurs + * + * @return none + */ +__attribute__((interrupt("WCH-Interrupt-fast"))) void HardFault_Handler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void HardFault_Handler(void) +{ + LOG_DUMP(); + + // asm("ebreak"); to trigger a breakpoint and test hardfault_handler + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, + "HardFault_Handler\r\n" + " SP=0x%08X\r\n" + " MIE=0x%08X\r\n" + " MSTATUS=0x%08X\r\n" + " MCAUSE=0x%08X\r\n" + " MVENDORID=0x%08X\r\n" + " MARCHID=0x%08X\r\n" + " MISA=0x%08X\r\n" + " MIMPID=0x%08X\r\n" + " MHARTID=0x%08X\r\n" + " MEPC=0x%08X\r\n" + " MSCRATCH=0x%08X\r\n" + " MTVEC=0x%08X\r\n", + __get_SP(), __get_MIE(), __get_MSTATUS(), __get_MCAUSE(), + __get_MVENDORID(), __get_MARCHID(), __get_MISA(), __get_MIMPID(), + __get_MHARTID(), __get_MEPC(), __get_MSCRATCH(), __get_MTVEC()); + + LOG_DUMP(); + + bsp_wait_ms_delay(100000000); +} diff --git a/tests/test_firmware_fifo/format.sh b/tests/test_firmware_fifo/format.sh new file mode 100644 index 0000000..6ec4ecb --- /dev/null +++ b/tests/test_firmware_fifo/format.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find ./User \( -iname "*.h" -o -iname "*.c" \) -print0 | xargs -0 clang-format --verbose --style=file -i; From 88b01882a0fdb7430ecec405d7c5b8e04f1608b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:25 +0200 Subject: [PATCH 06/10] Fix bounds check in pool_free --- src/wch-ch56x-lib/memory/pool.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wch-ch56x-lib/memory/pool.h b/src/wch-ch56x-lib/memory/pool.h index 7ec8d77..5577772 100644 --- a/src/wch-ch56x-lib/memory/pool.h +++ b/src/wch-ch56x-lib/memory/pool.h @@ -84,6 +84,7 @@ hydra_pool_get(hydra_pool_t* pool) return (void*)(pool->pool_members + i * pool->type_size); } } + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "Pool %x is full\r\n", pool); bsp_enable_interrupt(); return NULL; } @@ -98,7 +99,7 @@ hydra_pool_free(hydra_pool_t* pool, void* ptr) { uint16_t i = ((uint8_t*)(ptr)-pool->pool_members) / pool->type_size; bsp_disable_interrupt(); - if (i > pool->size || !pool->pool_manager[i]) + if (i >= pool->size || !pool->pool_manager[i]) { bsp_enable_interrupt(); return; From 62f9f7a264fe7803aad2c75524eccb6806751b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:26 +0200 Subject: [PATCH 07/10] Inline most ramx_pool functions --- src/wch-ch56x-lib/memory/ramx_alloc.c | 158 +---------------------- src/wch-ch56x-lib/memory/ramx_alloc.h | 176 ++++++++++++++++++++------ 2 files changed, 136 insertions(+), 198 deletions(-) diff --git a/src/wch-ch56x-lib/memory/ramx_alloc.c b/src/wch-ch56x-lib/memory/ramx_alloc.c index ef65567..31bbd31 100644 --- a/src/wch-ch56x-lib/memory/ramx_alloc.c +++ b/src/wch-ch56x-lib/memory/ramx_alloc.c @@ -17,14 +17,10 @@ limitations under the License. *******************************************************************************/ #include "wch-ch56x-lib/memory/ramx_alloc.h" -#include "wch-ch56x-lib/logging/logging.h" - -/* Taken from linux kernel */ -#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) __attribute__((aligned(16))) static uint8_t ramx_pool_buf[POOL_BUFFER_SIZE] __attribute__((section(".DMADATA"))); -static pool_t ramx_pool; +pool_t ramx_pool; /** * @brief Init pool allocator @@ -50,155 +46,3 @@ void ramx_pool_init(void) ramx_pool.blocks_reference_counter[i] = 0; } } - -/** - * @brief Allocates a number of contiguous blocks - * @param num_blocks: number of contiguous blocks to allocate - * @retval Pointer to the starting buffer, 0 if requested size is not available - */ -/* - * This function tries to allocate contiguous blocks by looking at the free - * blocks list. - * If found, it will mark the blocks as used (number of allocated blocks). - */ -void* ramx_pool_alloc_blocks(uint8_t num_blocks) -{ - uint32_t i, j; - uint8_t space_found; - - if (num_blocks == 0) - { - return 0; - } - if (num_blocks > POOL_BLOCK_NUM) - { - return 0; - } - - space_found = 1; - for (i = 0; i <= (uint32_t)POOL_BLOCK_NUM - num_blocks; i++) - { - if (ramx_pool.blocks[i] == 0) - { - for (j = 1; j < num_blocks; j++) - { - if (ramx_pool.blocks[i + j] != 0) - { - // i += j ? - space_found = 0; - break; - } - else - { - space_found = 1; - } - } - if (space_found == 1) - { - for (j = 0; j < num_blocks; j++) - { - ramx_pool.blocks[i + j] = num_blocks; - ramx_pool.blocks_reference_counter[i + j] = 1; - } - ramx_pool.blocks_used += num_blocks; - return ramx_pool.pool + (ramx_pool.block_size * i); - } - } - } - return 0; -} - -/** - * @brief Helper function to allocate a buffer of at least n bytes - * @param bytes: Number of bytes requested - * @retval Pointer to the starting buffer, 0 if requested size is not available - */ -/* - * This helper function will allocate enough pool blocks to store the requested - * number of bytes. - */ -void* ramx_pool_alloc_bytes(uint32_t num_bytes) -{ - LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_pool_alloc_bytes num_bytes %d free %d used %d \r\n", num_bytes, ramx_pool_stats_free(), ramx_pool_stats_used()); - uint8_t blocks_needed = DIV_ROUND_UP(num_bytes, POOL_BLOCK_SIZE); - return ramx_pool_alloc_blocks(blocks_needed); -} - -/** - * @brief Takes ownership of the blocks referenced by the pointer. - * ramx_pool_free must be called to release ownership and free the blocks if - * possible. - */ -void ramx_take_ownership(void* ptr) -{ - LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_take_ownership ptr %x free %d used %d \r\n", ptr, ramx_pool_stats_free(), ramx_pool_stats_used()); - - uint8_t block_index, num_blocks, i; - - if (ptr == 0) - { - return; - } - - block_index = - ((uint32_t)ptr - (uint32_t)ramx_pool.pool) / ramx_pool.block_size; - num_blocks = ramx_pool.blocks[block_index]; - - for (i = 0; i < num_blocks; i++) - { - ramx_pool.blocks_reference_counter[block_index + i] += 1; - } -} - -/** - * @brief Free owner from it's ownership on the blocks. Free the blocks if - * there are no more owners. - * @param ptr: pointer to the beginning of the buffer to be freed - * @retval None - */ - -void ramx_pool_free(void* ptr) -{ - LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_pool_free ptr %x free %d used %d \r\n", ptr, ramx_pool_stats_free(), ramx_pool_stats_used()); - - uint8_t block_index, num_blocks, i; - uint8_t num_freed = 0; - - if (ptr == 0) - { - return; - } - - block_index = - ((uint32_t)ptr - (uint32_t)ramx_pool.pool) / ramx_pool.block_size; - num_blocks = ramx_pool.blocks[block_index]; - - for (i = 0; i < num_blocks; i++) - { - if (ramx_pool.blocks_reference_counter[block_index + i] <= 1) - { - ramx_pool.blocks[block_index + i] = 0; - num_freed += 1; - } - else - { - ramx_pool.blocks_reference_counter[block_index + i] -= 1; - } - } - ramx_pool.blocks_used -= num_freed; -} - -bool ramx_address_in_pool(void* ptr) -{ - return (uint8_t*)ptr < ramx_pool.end_pool && - (uint8_t*)ptr >= ramx_pool.pool; -} - -uint8_t ramx_pool_stats_free() -{ - return ramx_pool.pool_size - ramx_pool.blocks_used; -} - -uint8_t ramx_pool_stats_used() { return ramx_pool.blocks_used; } - -uint8_t* ramx_pool_stats_blocks() { return ramx_pool.blocks; } diff --git a/src/wch-ch56x-lib/memory/ramx_alloc.h b/src/wch-ch56x-lib/memory/ramx_alloc.h index 99bec2d..a4ec5c3 100644 --- a/src/wch-ch56x-lib/memory/ramx_alloc.h +++ b/src/wch-ch56x-lib/memory/ramx_alloc.h @@ -22,6 +22,8 @@ limitations under the License. #include #include +#include "wch-ch56x-lib/logging/logging.h" + /** You must pass the following defines to your compiler #define POOL_BLOCK_SIZE size // the size of each individual block @@ -33,6 +35,9 @@ You must pass the following defines to your compiler extern "C" { #endif +/* Taken from linux kernel */ +#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) + typedef struct pool { uint8_t* pool; @@ -44,68 +49,157 @@ typedef struct pool uint8_t blocks_reference_counter[POOL_BLOCK_NUM]; // Blocks status } pool_t; +extern pool_t ramx_pool; /** * @brief Reset ramx pool state. * @param */ void ramx_pool_init(void); -/** - * @brief Allocate the min number of blocks that bytes can fit in. Not very - * efficient if bytes is not a multiple of block_size. - * @param bytes - * @return - */ -void* ramx_pool_alloc_bytes(uint32_t bytes); +__attribute__((always_inline)) static inline bool ramx_address_in_pool(void* ptr) +{ + return (uint8_t*)ptr < ramx_pool.end_pool && + (uint8_t*)ptr >= ramx_pool.pool; +} -/** - * @brief Allocate a number of blocks - * @param num_blocks - * @return - */ -void* ramx_pool_alloc_blocks(uint8_t num_blocks); +__attribute__((always_inline)) static inline uint8_t ramx_pool_stats_free() +{ + return ramx_pool.pool_size - ramx_pool.blocks_used; +} -/** - * @brief Increment the reference count on the blocks referenced by ptr. - * @param ptr - */ -void ramx_take_ownership(void* ptr); +__attribute__((always_inline)) static inline uint8_t ramx_pool_stats_used() { return ramx_pool.blocks_used; } -/** - * @brief Decreases the reference count on the blocks referenced by ptr, and - * deallocate them when the reference count goes to zero. - * @param ptr - */ -void ramx_pool_free(void* ptr); +__attribute__((always_inline)) static inline uint8_t* ramx_pool_stats_blocks() { return ramx_pool.blocks; } /** - * @brief Check if ptr is in the pool's address range. Useful to avoid - * unnecessary memcpy. - * @param ptr - * @return + * This function tries to allocate contiguous blocks by looking at the free + * blocks list. + * If found, it will mark the blocks as used (number of allocated blocks). + * @brief Allocates a number of contiguous blocks + * @param num_blocks: number of contiguous blocks to allocate + * @retval Pointer to the starting buffer, 0 if requested size is not available */ -bool ramx_address_in_pool(void* ptr); +__attribute__((always_inline)) static inline void* ramx_pool_alloc_blocks(uint8_t num_blocks) +{ + uint32_t i, j; + uint8_t space_found; + + if (num_blocks == 0) + { + return 0; + } + if (num_blocks > POOL_BLOCK_NUM) + { + return 0; + } + + space_found = 1; + for (i = 0; i <= (uint32_t)POOL_BLOCK_NUM - num_blocks; i++) + { + if (ramx_pool.blocks[i] == 0) + { + for (j = 1; j < num_blocks; j++) + { + if (ramx_pool.blocks[i + j] != 0) + { + // i += j ? + space_found = 0; + break; + } + else + { + space_found = 1; + } + } + if (space_found == 1) + { + for (j = 0; j < num_blocks; j++) + { + ramx_pool.blocks[i + j] = num_blocks; + ramx_pool.blocks_reference_counter[i + j] = 1; + } + ramx_pool.blocks_used += num_blocks; + return ramx_pool.pool + (ramx_pool.block_size * i); + } + } + } + return 0; +} /** - * @brief Get the number of free blocks. - * @param - * @return + * @brief Helper function to allocate a buffer of at least n bytes + * @param bytes: Number of bytes requested + * @retval Pointer to the starting buffer, 0 if requested size is not available */ -uint8_t ramx_pool_stats_free(void); +__attribute__((always_inline)) static inline void* ramx_pool_alloc_bytes(uint32_t num_bytes) +{ + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_pool_alloc_bytes num_bytes %d free %d used %d \r\n", num_bytes, ramx_pool_stats_free(), ramx_pool_stats_used()); + uint8_t blocks_needed = DIV_ROUND_UP(num_bytes, POOL_BLOCK_SIZE); + return ramx_pool_alloc_blocks(blocks_needed); +} /** - * @brief Get the number of used blocks. - * @param - * @return + * @brief Takes ownership of the blocks referenced by the pointer. + * ramx_pool_free must be called to release ownership and free the blocks if + * possible. */ -uint8_t ramx_pool_stats_used(void); +__attribute__((always_inline)) static inline void ramx_take_ownership(void* ptr) +{ + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_take_ownership ptr %x free %d used %d \r\n", ptr, ramx_pool_stats_free(), ramx_pool_stats_used()); + + uint8_t block_index, num_blocks, i; + + if (ptr == 0) + { + return; + } + + block_index = + ((uint32_t)ptr - (uint32_t)ramx_pool.pool) / ramx_pool.block_size; + num_blocks = ramx_pool.blocks[block_index]; + + for (i = 0; i < num_blocks; i++) + { + ramx_pool.blocks_reference_counter[block_index + i] += 1; + } +} /** - * @brief Get a pointer to the blocks states. - * @param - * @return + * @brief Free owner from it's ownership on the blocks. Free the blocks if + * there are no more owners. + * @param ptr: pointer to the beginning of the buffer to be freed + * @retval None */ -uint8_t* ramx_pool_stats_blocks(void); +__attribute__((always_inline)) static inline void ramx_pool_free(void* ptr) +{ + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "ramx_pool_free ptr %x free %d used %d \r\n", ptr, ramx_pool_stats_free(), ramx_pool_stats_used()); + + uint8_t block_index, num_blocks, i; + uint8_t num_freed = 0; + + if (ptr == 0) + { + return; + } + + block_index = + ((uint32_t)ptr - (uint32_t)ramx_pool.pool) / ramx_pool.block_size; + num_blocks = ramx_pool.blocks[block_index]; + + for (i = 0; i < num_blocks; i++) + { + if (ramx_pool.blocks_reference_counter[block_index + i] <= 1) + { + ramx_pool.blocks[block_index + i] = 0; + num_freed += 1; + } + else + { + ramx_pool.blocks_reference_counter[block_index + i] -= 1; + } + } + ramx_pool.blocks_used -= num_freed; +} #ifdef __cplusplus } From e684ff3804546a4f22270b12a6b297ae456e8cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:26 +0200 Subject: [PATCH 08/10] Inline interrupt queue functions, remove priority levels --- .../HSPIDeviceScheduled/hspi_scheduled.c | 8 +- .../interrupt_queue/interrupt_queue.c | 85 +------------------ .../interrupt_queue/interrupt_queue.h | 67 ++++++++++++--- .../User/test_interrupt_queue.h | 12 ++- 4 files changed, 67 insertions(+), 105 deletions(-) diff --git a/src/wch-ch56x-lib/HSPIDeviceScheduled/hspi_scheduled.c b/src/wch-ch56x-lib/HSPIDeviceScheduled/hspi_scheduled.c index cb57518..98785e0 100644 --- a/src/wch-ch56x-lib/HSPIDeviceScheduled/hspi_scheduled.c +++ b/src/wch-ch56x-lib/HSPIDeviceScheduled/hspi_scheduled.c @@ -331,7 +331,7 @@ bool hspi_send(uint8_t* buffer, uint16_t size, uint16_t custom_register) hspi_task_args->args.size = size; hspi_task_args->args.custom_register = custom_register; hydra_interrupt_queue_set_next_task(_hspi_send, (uint8_t*)hspi_task_args, - _hspi_cleanup, INTERRUPT_QUEUE_LOW_PRIO); + _hspi_cleanup); return true; } @@ -368,8 +368,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void HSPI_IRQHandler(void) hspi_task_args->args.custom_register = (udf0 & HSPI_USER_DEFINED_MASK) >> 13; hydra_interrupt_queue_set_next_task( - _hspi_rx_callback, (uint8_t*)hspi_task_args, _hspi_cleanup, - INTERRUPT_QUEUE_LOW_PRIO); + _hspi_rx_callback, (uint8_t*)hspi_task_args, _hspi_cleanup); } hspi_rx_buffer_0 = ramx_pool_alloc_bytes(hspi_packet_size); R32_HSPI_RX_ADDR0 = (vuint32_t)hspi_rx_buffer_0; @@ -391,8 +390,7 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void HSPI_IRQHandler(void) hspi_task_args->args.custom_register = (udf1 & HSPI_USER_DEFINED_MASK) >> 13; hydra_interrupt_queue_set_next_task( - _hspi_rx_callback, (uint8_t*)hspi_task_args, _hspi_cleanup, - INTERRUPT_QUEUE_LOW_PRIO); + _hspi_rx_callback, (uint8_t*)hspi_task_args, _hspi_cleanup); } hspi_rx_buffer_1 = ramx_pool_alloc_bytes(hspi_packet_size); R32_HSPI_RX_ADDR1 = (vuint32_t)hspi_rx_buffer_1; diff --git a/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.c b/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.c index 60dce2e..2f5bb15 100644 --- a/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.c +++ b/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.c @@ -16,92 +16,13 @@ limitations under the License. *******************************************************************************/ #include "wch-ch56x-lib/interrupt_queue/interrupt_queue.h" -#include "wch-ch56x-lib/logging/logging.h" -#include "wch-ch56x-lib/memory/fifo.h" -HYDRA_FIFO_DEF(task_queue_prio0, hydra_interrupt_queue_task_t, - INTERRUPT_QUEUE_SIZE); -HYDRA_FIFO_DEF(task_queue_prio1, hydra_interrupt_queue_task_t, +HYDRA_FIFO_DEF(task_queue, hydra_interrupt_queue_task_t, INTERRUPT_QUEUE_SIZE); -hydra_fifo_t* task_queues[] = { &task_queue_prio1, &task_queue_prio0 }; +hydra_fifo_t* task_queues[] = { &task_queue }; void hydra_interrupt_queue_init(void) { - fifo_clean(&task_queue_prio0); - fifo_clean(&task_queue_prio1); -} - -bool hydra_interrupt_queue_peek_next_task_prio0( - hydra_interrupt_queue_task_t* task) -{ - uint16_t read = fifo_peek_n(&task_queue_prio0, task, 1); - return read > 0 ? true : false; -} - -bool hydra_interrupt_queue_set_next_task(bool (*func)(uint8_t*), uint8_t* args, - void (*cleanup)(uint8_t*), - uint8_t prio) -{ - LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, - "hydra_interrupt_queue_set_next_task prio %d\r\n", prio); - hydra_interrupt_queue_task_t task; - task.task = func; - task.args = args; - task.cleanup = cleanup; - - uint16_t written = 0; - if (prio == 0) - { - written = fifo_write(&task_queue_prio0, &task, 1); - } - else if (prio == 1) - { - written = fifo_write(&task_queue_prio1, &task, 1); - } - else - { - return false; - } - if (written == 0) - return false; - - return true; -} - -void hydra_interrupt_queue_run(void) -{ - for (size_t i = 0; i < sizeof(task_queues) / sizeof(hydra_fifo_t*); ++i) - { - hydra_interrupt_queue_task_t task; - LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, - "hydra_interrupt_queue_run prio %d \r\n", - sizeof(task_queues) / sizeof(hydra_fifo_t*) - 1 - i); - if (fifo_read_n(task_queues[i], &task, 1)) - { - LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, - "hydra_interrupt_queue_run prio %d executing task\r\n", - sizeof(task_queues) / sizeof(hydra_fifo_t*) - 1 - i); - task.task(task.args); - if (task.cleanup) - task.cleanup(task.args); - break; - } - } -} - -void hydra_interrupt_queue_free_all(void) -{ - for (size_t i = 0; i < sizeof(task_queues) / sizeof(hydra_fifo_t*); ++i) - { - hydra_interrupt_queue_task_t task; - while (fifo_read_n(task_queues[i], &task, 1)) - { - // LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, - // "interrupt_queue_get_next_task prio %d \r\n", sizeof(task_queues) / - // sizeof(hydra_fifo_t*) - 1 - i); - if (task.cleanup) - task.cleanup(task.args); - } - } + fifo_clean(&task_queue); } diff --git a/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.h b/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.h index ba186e0..eca5a8f 100644 --- a/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.h +++ b/src/wch-ch56x-lib/interrupt_queue/interrupt_queue.h @@ -32,8 +32,8 @@ limitations under the License. #include #include -#define INTERRUPT_QUEUE_LOW_PRIO 0 -#define HYDRA_INTERRUPT_QUEUE_HIGH_PRIO 1 +#include "wch-ch56x-lib/logging/logging.h" +#include "wch-ch56x-lib/memory/fifo.h" #ifdef __cplusplus extern "C" { @@ -47,6 +47,9 @@ typedef struct HYDRA_INTERRUPT_QUEUE_TASK void (*cleanup)(uint8_t*); } hydra_interrupt_queue_task_t; +HYDRA_FIFO_DECLR(task_queue, hydra_interrupt_queue_task_t, + INTERRUPT_QUEUE_SIZE); + /** * @brief Initialize (or reset) internal state of interrupt_queue. * @param @@ -58,12 +61,33 @@ void hydra_interrupt_queue_init(void); * HYDRA_INTERRUPT_QUEUE_HIGH_PRIO queue, then INTERRUPT_QUEUE_LOW_PRIO. * @param */ -void hydra_interrupt_queue_run(void); +__attribute__((always_inline)) static inline void hydra_interrupt_queue_run(void) +{ + hydra_interrupt_queue_task_t task; + LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, + "hydra_interrupt_queue_run \r\n"); + if (fifo_read_n(&task_queue, &task, 1)) + { + LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, + "hydra_interrupt_queue_run executing task\r\n"); + task.task(task.args); + if (task.cleanup) + task.cleanup(task.args); + } +} /** * @brief Free all task from interrupt_queue and call their cleanup task. */ -void hydra_interrupt_queue_free_all(void); +__attribute__((always_inline)) static inline void hydra_interrupt_queue_free_all(void) +{ + hydra_interrupt_queue_task_t task; + while (fifo_read_n(&task_queue, &task, 1)) + { + if (task.cleanup) + task.cleanup(task.args); + } +} /** * @brief Set next task @@ -73,13 +97,30 @@ void hydra_interrupt_queue_free_all(void); * is scheduled * @param cleanup Functions called after func has been executed or when freeing * all tasks. Allows cleaning up args for instance. - * @param prio Either INTERRUPT_QUEUE_LOW_PRIO or - * HYDRA_INTERRUPT_QUEUE_HIGH_PRIO. * @return */ -bool hydra_interrupt_queue_set_next_task(bool (*func)(uint8_t*), uint8_t* args, - void (*cleanup)(uint8_t*), - uint8_t prio); +__attribute__((always_inline)) static inline bool hydra_interrupt_queue_set_next_task(bool (*func)(uint8_t*), uint8_t* args, + void (*cleanup)(uint8_t*)) +{ + LOG_IF(LOG_LEVEL_TRACE, LOG_ID_INTERRUPT_QUEUE, + "hydra_interrupt_queue_set_next_task %d\r\n"); + hydra_interrupt_queue_task_t task; + task.task = func; + task.args = args; + task.cleanup = cleanup; + + uint16_t written = 0; + + written = fifo_write(&task_queue, &task, 1); + + if (written == 0) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "Interrupt queue is full\r\n"); + return false; + } + + return true; +} /** * @brief Peek the next task to be scheduled, to check which function is coming @@ -87,8 +128,12 @@ bool hydra_interrupt_queue_set_next_task(bool (*func)(uint8_t*), uint8_t* args, * @param task task address where the next task will be copied * @return */ -bool hydra_interrupt_queue_peek_next_task_prio0( - hydra_interrupt_queue_task_t* task); +__attribute__((always_inline)) static inline bool hydra_interrupt_queue_peek_next_task_prio0( + hydra_interrupt_queue_task_t* task) +{ + uint16_t read = fifo_peek_n(&task_queue, task, 1); + return read > 0 ? true : false; +} #ifdef __cplusplus } diff --git a/tests/test_firmware_unittests/User/test_interrupt_queue.h b/tests/test_firmware_unittests/User/test_interrupt_queue.h index ae10e18..6de61f4 100644 --- a/tests/test_firmware_unittests/User/test_interrupt_queue.h +++ b/tests/test_firmware_unittests/User/test_interrupt_queue.h @@ -49,11 +49,11 @@ bool test_interrupt_queue_set_tasks(void) hydra_interrupt_queue_init(); hydra_interrupt_queue_set_next_task(some_random_function, (uint8_t*)&num_run, - NULL, INTERRUPT_QUEUE_LOW_PRIO); + NULL); hydra_interrupt_queue_set_next_task(some_random_function, (uint8_t*)&num_run, - NULL, INTERRUPT_QUEUE_LOW_PRIO); + NULL); hydra_interrupt_queue_set_next_task(some_random_function, (uint8_t*)&num_run, - NULL, INTERRUPT_QUEUE_LOW_PRIO); + NULL); hydra_interrupt_queue_run(); hydra_interrupt_queue_run(); @@ -75,8 +75,7 @@ bool test_interrupt_queue_overflow(void) for (int i = 0; i < INTERRUPT_QUEUE_SIZE + 5; ++i) { result = hydra_interrupt_queue_set_next_task(some_random_function, - (uint8_t*)&num_run, NULL, - INTERRUPT_QUEUE_LOW_PRIO); + (uint8_t*)&num_run, NULL); } for (int i = 0; i < INTERRUPT_QUEUE_SIZE + 5; ++i) @@ -101,8 +100,7 @@ bool test_interrupt_queue_stress(void) for (int i = 0; i < INTERRUPT_QUEUE_SIZE; ++i) { result = hydra_interrupt_queue_set_next_task(some_random_function, - (uint8_t*)&num_run, NULL, - INTERRUPT_QUEUE_LOW_PRIO); + (uint8_t*)&num_run, NULL); if (!result) return false; } From 4118ebea3377d15a9484af7400667db69b371a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:26 +0200 Subject: [PATCH 09/10] Add full usb2 control out support, fix zlp and toggles, move back to ep0 fixed buffer, remove usb3 irq from usb2 --- src/wch-ch56x-lib/USBDevice/usb20.c | 533 ++++++++++++----------- src/wch-ch56x-lib/USBDevice/usb_device.h | 20 +- 2 files changed, 288 insertions(+), 265 deletions(-) diff --git a/src/wch-ch56x-lib/USBDevice/usb20.c b/src/wch-ch56x-lib/USBDevice/usb20.c index e8b91d5..05f1195 100644 --- a/src/wch-ch56x-lib/USBDevice/usb20.c +++ b/src/wch-ch56x-lib/USBDevice/usb20.c @@ -18,7 +18,6 @@ limitations under the License. *******************************************************************************/ #include "wch-ch56x-lib/USBDevice/usb20.h" -#include "wch-ch56x-lib/USBDevice/usb30.h" #include "wch-ch56x-lib/USBDevice/usb_device.h" #include "wch-ch56x-lib/USBDevice/usb_endpoints.h" @@ -28,21 +27,16 @@ limitations under the License. ENDPOINT_12_TX | ENDPOINT_12_RX | ENDPOINT_13_TX | ENDPOINT_13_RX | \ ENDPOINT_14_TX | ENDPOINT_14_RX | ENDPOINT_15_TX | ENDPOINT_15_RX)) #define USB2_ENDP0_MAX_PACKET_SIZE ((uint8_t)(64)) +#define USB2_EP_MAX_PACKET_SIZE ((uint16_t)(1024)) // the default device is a regular USB3 with the mandatory USB2 compatibility. // however, it is possible to separate the USB2 and USB3 devices so that they use different devices. usb_device_t* usb2_backend_current_device = &usb_device_0; static volatile puint8_t desc_head = NULL; +static volatile uint16_t endp0_current_transfer_size = 0; static volatile uint16_t endp0_remaining_bytes = 0; -static volatile uint16_t endp0_tx_remaining_bytes = 0; -static volatile uint16_t endp1_tx_remaining_bytes = 0; -static volatile uint16_t endp2_tx_remaining_bytes = 0; -static volatile uint16_t endp3_tx_remaining_bytes = 0; -static volatile uint16_t endp4_tx_remaining_bytes = 0; -static volatile uint16_t endp5_tx_remaining_bytes = 0; -static volatile uint16_t endp6_tx_remaining_bytes = 0; -static volatile uint16_t endp7_tx_remaining_bytes = 0; +static volatile uint16_t endp_tx_remaining_bytes[8]; static volatile USB_SETUP usb_setup_req; static volatile bool ep0_passthrough_enabled = false; @@ -108,6 +102,12 @@ void usb2_setup_endpoints(void) R8_UEP0_TX_CTRL = UEP_T_RES_NAK | RB_UEP_T_TOG_0; R8_UEP0_RX_CTRL = UEP_R_RES_ACK | RB_UEP_R_TOG_0; + if (usb2_backend_current_device->endpoint_mask & (USB2_UNSUPPORTED_ENDPOINTS)) + { + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Unsupported endpoints \r\n"); + return; + } + if (usb2_backend_current_device->endpoint_mask & ENDPOINT_1_TX) { R8_UEP4_1_MOD |= RB_UEP1_TX_EN; @@ -118,6 +118,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_1_RX) { + if (usb2_backend_current_device->endpoints.rx[1].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 1, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP4_1_MOD |= RB_UEP1_RX_EN; R32_UEP1_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[1].buffer; R16_UEP1_MAX_LEN = usb2_backend_current_device->endpoints.rx[1].max_packet_size; @@ -135,6 +140,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_2_RX) { + if (usb2_backend_current_device->endpoints.rx[2].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 2, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP2_3_MOD |= RB_UEP2_RX_EN; R32_UEP2_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[2].buffer; R16_UEP2_MAX_LEN = usb2_backend_current_device->endpoints.rx[2].max_packet_size; @@ -152,6 +162,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_3_RX) { + if (usb2_backend_current_device->endpoints.rx[3].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 3, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP2_3_MOD |= RB_UEP3_RX_EN; R32_UEP3_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[3].buffer; R16_UEP3_MAX_LEN = usb2_backend_current_device->endpoints.rx[3].max_packet_size; @@ -169,6 +184,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_4_RX) { + if (usb2_backend_current_device->endpoints.rx[4].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 4, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP4_1_MOD |= RB_UEP4_RX_EN; R32_UEP4_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[4].buffer; R16_UEP4_MAX_LEN = usb2_backend_current_device->endpoints.rx[4].max_packet_size; @@ -186,6 +206,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_5_RX) { + if (usb2_backend_current_device->endpoints.rx[5].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 5, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP5_6_MOD |= RB_UEP5_RX_EN; R32_UEP5_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[5].buffer; R16_UEP5_MAX_LEN = usb2_backend_current_device->endpoints.rx[5].max_packet_size; @@ -203,6 +228,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_6_RX) { + if (usb2_backend_current_device->endpoints.rx[6].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 6, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP5_6_MOD |= RB_UEP6_RX_EN; R32_UEP6_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[6].buffer; R16_UEP6_MAX_LEN = usb2_backend_current_device->endpoints.rx[6].max_packet_size; @@ -220,6 +250,11 @@ void usb2_setup_endpoints(void) } if (usb2_backend_current_device->endpoint_mask & ENDPOINT_7_RX) { + if (usb2_backend_current_device->endpoints.rx[7].max_packet_size > USB2_EP_MAX_PACKET_SIZE) + { + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, "ep %d rx max_packet_size exceeds the %d bytes limit \r\n", 7, USB2_EP_MAX_PACKET_SIZE); + return; + } R8_UEP7_MOD |= RB_UEP7_RX_EN; R32_UEP7_RX_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[7].buffer; R16_UEP7_MAX_LEN = usb2_backend_current_device->endpoints.rx[7].max_packet_size; @@ -229,7 +264,7 @@ void usb2_setup_endpoints(void) if (usb2_backend_current_device->endpoint_mask & (USB2_UNSUPPORTED_ENDPOINTS)) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Unsupported endpoints \r\n"); + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Unsupported endpoints, mask %x \r\n", usb2_backend_current_device->endpoint_mask & (USB2_UNSUPPORTED_ENDPOINTS)); } } @@ -249,11 +284,11 @@ void usb2_ep0_passthrough_enabled(bool enable) __attribute__((always_inline)) inline void usb2_ep0_set_configuration_callback(void) { - if (usb2_backend_current_device->current_config == 0) - { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "ERROR : config should be > 0 \r\n"); - return; - } + // if (usb2_backend_current_device->current_config == 0) + // { + // LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "ERROR : config should be > 0 \r\n"); + // return; + // } usb2_reset_endpoints(); } @@ -283,154 +318,135 @@ usb2_ep0_request_handler(void) LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Received SETUP stage of CTRL transfer \r\n"); - if (usb_setup_req.bRequest == USB_GET_DESCRIPTOR) + if (((usb_setup_req.bRequestType & USB_REQ_TYP_MASK) >> 6) == USB_REQ_TYP_STANDARD) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "Received GET_DESCRIPTOR standard request, descriptor %d \r\n", - usb_setup_req.wValue.bw.bb0); - switch (usb_setup_req.wValue.bw.bb0) + if (usb_setup_req.bRequest == USB_GET_DESCRIPTOR) { - case USB_DESCR_TYP_DEVICE: - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "DEVICE DESCRIPTOR \r\n"); - if (usb2_backend_current_device->usb_descriptors.usb2_device_descr != NULL) - { - desc_head = (puint8_t)usb2_backend_current_device->usb_descriptors.usb2_device_descr; - endp0_remaining_bytes = - usb2_backend_current_device->usb_descriptors.usb2_device_descr->bLength > - usb_setup_req.wLength - ? usb_setup_req.wLength - : usb2_backend_current_device->usb_descriptors.usb2_device_descr->bLength; - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "size of device descr %d \r\n", - endp0_remaining_bytes); - len = usb2_ep0_next_data_packet_size(); - memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); - } - break; - case USB_DESCR_TYP_CONFIG: - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "CONFIG DESCRIPTOR \r\n"); - if (usb2_backend_current_device->usb_descriptors.usb2_device_config_descrs[0] != NULL) - { - uint16_t usb_config_descr_total_size = - ((USB_CFG_DESCR*)(usb2_backend_current_device->usb_descriptors - .usb2_device_config_descrs[0])) - ->wTotalLength; - desc_head = - (puint8_t)usb2_backend_current_device->usb_descriptors.usb2_device_config_descrs[0]; - endp0_remaining_bytes = - usb_config_descr_total_size > usb_setup_req.wLength - ? usb_setup_req.wLength - : usb_config_descr_total_size; - len = usb2_ep0_next_data_packet_size(); - memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); - } - break; - case USB_DESCR_TYP_STRING: - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "STRING DESCRIPTOR \r\n"); - if (usb2_backend_current_device->usb_descriptors.usb_string_descrs != NULL) + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, + "Received GET_DESCRIPTOR standard request, descriptor %d \r\n", + usb_setup_req.wValue.bw.bb0); + switch (usb_setup_req.wValue.bw.bb0) { - uint8_t str_descr_index = usb_setup_req.wValue.bw.bb1; - uint16_t usb_str_descr_total_size = - ((USB_STRING_DESCR*)(usb2_backend_current_device->usb_descriptors - .usb_string_descrs[str_descr_index])) - ->bLength; - desc_head = - (puint8_t) - usb2_backend_current_device->usb_descriptors.usb_string_descrs[str_descr_index]; - endp0_remaining_bytes = usb_str_descr_total_size; - len = usb2_ep0_next_data_packet_size(); - memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + case USB_DESCR_TYP_DEVICE: + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "DEVICE DESCRIPTOR \r\n"); + if (usb2_backend_current_device->usb_descriptors.usb2_device_descr != NULL) + { + desc_head = (puint8_t)usb2_backend_current_device->usb_descriptors.usb2_device_descr; + endp0_remaining_bytes = + usb2_backend_current_device->usb_descriptors.usb2_device_descr->bLength > + usb_setup_req.wLength + ? usb_setup_req.wLength + : usb2_backend_current_device->usb_descriptors.usb2_device_descr->bLength; + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "size of device descr %d \r\n", + endp0_remaining_bytes); + len = usb2_ep0_next_data_packet_size(); + memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + } + break; + case USB_DESCR_TYP_CONFIG: + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "CONFIG DESCRIPTOR \r\n"); + if (usb2_backend_current_device->usb_descriptors.usb2_device_config_descrs[0] != NULL) + { + uint16_t usb_config_descr_total_size = + ((USB_CFG_DESCR*)(usb2_backend_current_device->usb_descriptors + .usb2_device_config_descrs[0])) + ->wTotalLength; + desc_head = + (puint8_t)usb2_backend_current_device->usb_descriptors.usb2_device_config_descrs[0]; + endp0_remaining_bytes = + usb_config_descr_total_size > usb_setup_req.wLength + ? usb_setup_req.wLength + : usb_config_descr_total_size; + len = usb2_ep0_next_data_packet_size(); + memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + } + break; + case USB_DESCR_TYP_STRING: + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "STRING DESCRIPTOR \r\n"); + if (usb2_backend_current_device->usb_descriptors.usb_string_descrs != NULL) + { + uint8_t str_descr_index = usb_setup_req.wValue.bw.bb1; + uint16_t usb_str_descr_total_size = + ((USB_STRING_DESCR*)(usb2_backend_current_device->usb_descriptors + .usb_string_descrs[str_descr_index])) + ->bLength; + desc_head = + (puint8_t) + usb2_backend_current_device->usb_descriptors.usb_string_descrs[str_descr_index]; + endp0_remaining_bytes = usb_str_descr_total_size; + len = usb2_ep0_next_data_packet_size(); + memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + } + break; + default: + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, + "Descriptor not currently supported \r\n"); + len = 0xffff; + break; } - break; - default: - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "Descriptor not currently supported \r\n"); - len = 0xffff; - break; } - } - else if (usb_setup_req.bRequest == USB_SET_ADDRESS) - { - usb2_backend_current_device->addr = usb_setup_req.wValue.bw.bb1; - endp0_remaining_bytes = usb_setup_req.wLength; - } - else if (usb_setup_req.bRequest == USB_SET_CONFIGURATION) - { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "Received SET_CONFIGURATION standard request, config %d \r\n", - usb_setup_req.wValue.bw.bb1); - usb2_backend_current_device->current_config = usb_setup_req.wValue.bw.bb1; - usb2_ep0_set_configuration_callback(); - if (usb_setup_req.wValue.bw.bb1 != 0) - usb2_backend_current_device->state = CONFIGURED; - } - else if (usb_setup_req.bRequest == USB_SET_FEATURE) - { - if (usb_setup_req.wValue.w == - ENDPOINT_HALT) // reset endpoint toggle to DATA0 + else if (usb_setup_req.bRequest == USB_SET_ADDRESS) { - usb2_reset_endpoints(); + usb2_backend_current_device->addr = usb_setup_req.wValue.bw.bb1; + endp0_remaining_bytes = usb_setup_req.wLength; } - } - else - { - uint8_t* buffer = NULL; - len = usb2_backend_current_device->endpoints.endp0_user_handled_control_request( - (USB_SETUP*)&usb_setup_req, &buffer); - - if (len > 0) + else if (usb_setup_req.bRequest == USB_SET_CONFIGURATION) + { + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, + "Received SET_CONFIGURATION standard request, config %d \r\n", + usb_setup_req.wValue.bw.bb1); + usb2_backend_current_device->current_config = usb_setup_req.wValue.bw.bb1; + usb2_ep0_set_configuration_callback(); + if (usb_setup_req.wValue.bw.bb1 != 0) + usb2_backend_current_device->state = CONFIGURED; + } + else if (usb_setup_req.bRequest == USB_SET_FEATURE) { - if (buffer == NULL) + if (usb_setup_req.wValue.w == + ENDPOINT_HALT) // reset endpoint toggle to DATA0 { - return 0xffff; + usb2_reset_endpoints(); } - desc_head = (puint8_t)buffer; - endp0_remaining_bytes = len; - len = usb2_ep0_next_data_packet_size(); - memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); } } - - return len; -} - -/** - * @fn usb2_ep0_in_prepare_next - * @brief Called by usb2_ep0_in_handler. This marks the end of an IN transfer - *(data from device to host), and returns the length of data left to be sent. - **/ -__attribute__((always_inline)) static inline uint16_t -usb2_ep0_in_prepare_next(void) -{ - switch (usb_setup_req.bRequest) + else { - case USB_GET_DESCRIPTOR: { - uint16_t len = usb2_ep0_next_data_packet_size(); - endp0_remaining_bytes -= len; - desc_head += len; - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "ENDP0 IN Callback remaining_bytes : %d\r\n", endp0_remaining_bytes); - } - return usb2_ep0_next_data_packet_size(); - break; - case USB_SET_ADDRESS: - // Even though the address is received in the SETUP stage, the STATUS stage - // must be finished with address 0 and new address set after that. - if (usb2_set_device_address(usb2_backend_current_device->addr)) + if (usb_setup_req.bRequestType & USB_REQ_TYP_IN) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Set address to %d \r\n", - usb2_backend_current_device->addr); - usb2_backend_current_device->state = ADDRESS; + uint8_t* buffer = NULL; + len = usb2_backend_current_device->endpoints.endp0_user_handled_control_request( + (USB_SETUP*)&usb_setup_req, &buffer); + if (len > 0) + { + if (buffer == NULL) + { + return 0xffff; + } + desc_head = (puint8_t)buffer; + endp0_remaining_bytes = len; + len = usb2_ep0_next_data_packet_size(); + memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + } } else { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Failed to set address to %d \r\n", - usb2_backend_current_device->addr); + if (usb_setup_req.wLength == 0) + { + usb2_backend_current_device->endpoints.endp0_user_handled_control_request( + (USB_SETUP*)&usb_setup_req, NULL); + return 0; + } + // there should be an enforcement for the max size ep0 can receive + // else if (usb2_backend_current_device->endpoints.rx[0].max_packet_size_with_burst < usb_setup_req.wLength) + // { + // return 0xffff; + // } + endp0_remaining_bytes = usb_setup_req.wLength; + len = usb_setup_req.wLength; } - break; - default: - break; } - return 0; + endp0_current_transfer_size = endp0_remaining_bytes; + return len; } /** @@ -442,32 +458,35 @@ __attribute__((always_inline)) static inline void usb2_ep0_setup_stage_handler(void) { uint16_t len = usb2_ep0_request_handler(); - if (len != 0xffff) - { - // either sends data, or ZLP (zero length packet)(in case there is no data - // packet, like for Set_Address) - R16_UEP0_T_LEN = len; - R8_UEP0_TX_CTRL = - UEP_T_RES_ACK | - RB_UEP_T_TOG_1; // endp0 is in fact constituted of two endpoints : ENDP0 - // IN and ENDP0 OUT. Therefore, they both have their own - // toggle bit - R8_UEP0_RX_CTRL = - UEP_R_RES_ACK | - RB_UEP_R_TOG_1; // here we set both toggle bits to DATA1, for either the - // DATA IN or DATA OUT stage - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "R8_UEP0_TX_CTRL "); - // LOG_8_BITS_REGISTER((puint8_t)&R8_UEP0_TX_CTRL); - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "R8_UEP0_RX_CTRL "); - // LOG_8_BITS_REGISTER((puint8_t)&R8_UEP0_RX_CTRL); + if (usb_setup_req.bRequestType & USB_REQ_TYP_IN) + { + if (len != 0xffff) + { + // send data + R16_UEP0_T_LEN = len; + R8_UEP0_TX_CTRL = + UEP_T_RES_ACK | + RB_UEP_T_TOG_1; + } + else + { + R16_UEP0_T_LEN = 0; + R8_UEP0_TX_CTRL = UEP_T_RES_STALL; + R8_UEP0_RX_CTRL = + UEP_R_RES_ACK | RB_UEP_R_TOG_1; //prepare Status stage + } } else { - // Unsupported request - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "stall \r\n"); - R16_UEP0_T_LEN = 0; - R8_UEP0_TX_CTRL = UEP_T_RES_STALL; - R8_UEP0_RX_CTRL = UEP_R_RES_STALL; + if (usb_setup_req.wLength == 0) + { + R16_UEP0_T_LEN = 0; + R8_UEP0_TX_CTRL = UEP_T_RES_ACK | RB_UEP_T_TOG_1; // prepare Status stage + } + else + { + R8_UEP0_RX_CTRL = UEP_R_RES_ACK | RB_UEP_R_TOG_1; + } } } @@ -478,38 +497,65 @@ usb2_ep0_setup_stage_handler(void) **/ __attribute__((always_inline)) static inline void usb2_ep0_in_handler(void) { - uint16_t len = usb2_ep0_in_prepare_next(); // transmission complete - if (len == 0) + if (usb_setup_req.bRequestType & USB_REQ_TYP_IN) { - if (usb_setup_req.bRequestType & USB_REQ_TYP_IN) + uint16_t len = usb2_ep0_next_data_packet_size(); + endp0_remaining_bytes -= len; + desc_head += len; + + if (endp0_remaining_bytes == 0) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "PID IN transmission complete \r\n"); - R16_UEP0_T_LEN = 0; - R8_UEP0_TX_CTRL = UEP_T_RES_STALL; - R8_UEP0_RX_CTRL = UEP_R_RES_ACK | - RB_UEP_R_TOG_1; // prepare for Status stage, with DATA - // OUT sending ZLP (zero-length packet) - desc_head = 0; + // Do not forget ZLP in case we only send full-sized packets : the host knows when the transfer is finished + // either by receiving a short packet (why would the device send a short packet if it has more to send ?) or a ZLP (zero-length packet) + if ((endp0_current_transfer_size % USB2_ENDP0_MAX_PACKET_SIZE) == 0 && len != 0) + { + R16_UEP0_T_LEN = 0; + R8_UEP0_TX_CTRL = (R8_UEP0_TX_CTRL & ~(uint8_t)RB_UEP_TRES_MASK) | (uint8_t)UEP_T_RES_ACK; + R8_UEP0_TX_CTRL ^= RB_UEP_T_TOG_1; // alternate between DATA0 and DATA1 + } + else + { + desc_head = NULL; + R8_UEP0_TX_CTRL = (R8_UEP0_TX_CTRL & ~(uint8_t)RB_UEP_TRES_MASK) | (uint8_t)UEP_T_RES_NAK; + R8_UEP0_RX_CTRL = UEP_R_RES_ACK | RB_UEP_R_TOG_1; // prepare Status stage + } } else { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "Received STATUS for OUT transaction \r\n"); - R16_UEP0_T_LEN = 0; - R8_UEP0_TX_CTRL = UEP_T_RES_STALL; // nothing to transmit - R8_UEP0_RX_CTRL = UEP_R_RES_ACK | RB_UEP_R_TOG_0; // keep listening + len = usb2_ep0_next_data_packet_size(); + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Sending next %d bytes endp0 \r\n", + len); + memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); + R16_UEP0_T_LEN = len; + R8_UEP0_TX_CTRL = + (R8_UEP0_TX_CTRL & ~(uint8_t)RB_UEP_TRES_MASK) | (uint8_t)UEP_T_RES_ACK; + R8_UEP0_TX_CTRL ^= RB_UEP_T_TOG_1; // alternate between DATA0 and DATA1 } } else { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Sending next %d bytes endp0 \r\n", - len); - memcpy(usb2_backend_current_device->endpoints.rx[0].buffer, desc_head, len); - R16_UEP0_T_LEN = len; - R8_UEP0_TX_CTRL ^= RB_UEP_T_TOG_1; // alternate between DATA0 and DATA1 - R8_UEP0_TX_CTRL = - (R8_UEP0_TX_CTRL & ~(uint8_t)RB_UEP_TRES_MASK) | (uint8_t)UEP_T_RES_ACK; + if (usb_setup_req.bRequest == USB_SET_ADDRESS) + { + // Even though the address is received in the SETUP stage, the STATUS stage + // must be finished with address 0 and new address set after that. + if (usb2_set_device_address(usb2_backend_current_device->addr)) + { + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Set address to %d \r\n", + usb2_backend_current_device->addr); + usb2_backend_current_device->state = ADDRESS; + } + else + { + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Failed to set address to %d \r\n", + usb2_backend_current_device->addr); + } + } + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, + "Received STATUS for OUT transaction \r\n"); + R16_UEP0_T_LEN = 0; + R8_UEP0_TX_CTRL = (R8_UEP0_TX_CTRL & ~(uint8_t)RB_UEP_TRES_MASK) | UEP_T_RES_NAK; // nothing to transmit + R8_UEP0_RX_CTRL = UEP_T_RES_ACK | RB_UEP_T_TOG_1; // keep listening } } @@ -524,72 +570,43 @@ __attribute__((always_inline)) static inline void usb2_ep0_out_handler(void) { LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Received STATUS for IN transaction \r\n"); - // LOG_USB_SETUP_REQ((USB_SETUP*)&usb_setup_req); // process Status stage if (usb2_ep0_next_data_packet_size() > 0) { LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "setup command not finished\r\n"); - R8_UEP0_RX_CTRL = UEP_R_RES_NAK | RB_UEP_R_TOG_1; + R8_UEP0_RX_CTRL = (R8_UEP0_RX_CTRL & ~RB_UEP_TRES_MASK) | UEP_T_RES_NAK; } else { LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "setup command finished\r\n"); - R8_UEP0_RX_CTRL = UEP_T_RES_STALL; + R8_UEP0_RX_CTRL = UEP_R_RES_ACK; R16_UEP0_T_LEN = 0; - R8_UEP0_TX_CTRL = 0; - usb2_backend_current_device->endpoints.tx_complete[0](Ack); + R8_UEP0_TX_CTRL = (R8_UEP0_TX_CTRL & ~RB_UEP_TRES_MASK) | UEP_T_RES_NAK; } } else { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "PID OUT transmission complete, preparing for IN STATUS \r\n"); - R16_UEP0_T_LEN = - 0; // sending a ZLP for the DATA IN packet of the STATUS stage - R8_UEP0_RX_CTRL = UEP_R_RES_STALL; - R8_UEP0_TX_CTRL = UEP_T_RES_ACK | RB_UEP_T_TOG_1; - } -} + endp0_remaining_bytes -= R16_USB_RX_LEN; -/** - * @brief Get the remaining number of bytes left to send - * @param endp_num endpoint number - * @return - */ -__attribute__((always_inline)) static inline vuint16_t* -usb2_get_tx_endpoint_remaining_len(uint8_t endp_num) -{ - switch (endp_num) - { - case ENDP_0: - return &endp0_tx_remaining_bytes; - case ENDP_1: - return &endp1_tx_remaining_bytes; - break; - case ENDP_2: - return &endp2_tx_remaining_bytes; - break; - case ENDP_3: - return &endp3_tx_remaining_bytes; - break; - case ENDP_4: - return &endp4_tx_remaining_bytes; - break; - case ENDP_5: - return &endp5_tx_remaining_bytes; - break; - case ENDP_6: - return &endp6_tx_remaining_bytes; - break; - case ENDP_7: - return &endp7_tx_remaining_bytes; - break; - default: - return NULL; - break; + if (endp0_remaining_bytes == 0) + { + R32_UEP0_RT_DMA = (uint32_t)(uint8_t*)usb2_backend_current_device->endpoints.rx[0].buffer; + LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, + "PID OUT transmission complete, preparing for IN STATUS \r\n"); + R16_UEP0_T_LEN = + 0; // sending a ZLP for the DATA IN packet of the STATUS stage + R8_UEP0_TX_CTRL = UEP_T_RES_ACK | RB_UEP_T_TOG_1; + usb2_backend_current_device->endpoints.endp0_user_handled_control_request( + (USB_SETUP*)&usb_setup_req, NULL); + } + else + { + R32_UEP0_RT_DMA += R16_USB_RX_LEN; + R8_UEP0_RX_CTRL = (R8_UEP0_RX_CTRL & ~RB_UEP_TRES_MASK) | UEP_T_RES_ACK; + R8_UEP0_RX_CTRL ^= RB_UEP_R_TOG_1; // switch between DATA0/DATA1 toggle + } } - return NULL; } /** @@ -603,7 +620,7 @@ usb2_in_transfer_handler(uint8_t endp_num) volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.tx[endp_num]; vuint8_t* TX_CTRL = usb2_get_tx_endpoint_ctrl_reg(endp_num); vuint16_t* T_Len = usb2_get_tx_endpoint_len_reg(endp_num); - vuint16_t* tx_remaining_bytes = usb2_get_tx_endpoint_remaining_len(endp_num); + vuint16_t* tx_remaining_bytes = &endp_tx_remaining_bytes[endp_num]; #ifdef DEBUG if (endp == NULL || TX_CTRL == NULL || T_Len == NULL || @@ -620,8 +637,6 @@ usb2_in_transfer_handler(uint8_t endp_num) if (*tx_remaining_bytes == 0) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, - "End of IN transfer for ENDP %d \r\n", endp_num); *T_Len = 0; if (endp_num != 0) { @@ -638,8 +653,6 @@ usb2_in_transfer_handler(uint8_t endp_num) { *TX_CTRL = (*TX_CTRL & ~RB_UEP_TRES_MASK) | UEP_T_RES_ACK; } - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "ENDP %d TX Remaining bytes : %d \r\n", - endp_num, *tx_remaining_bytes); } else { @@ -656,6 +669,7 @@ usb2_in_transfer_handler(uint8_t endp_num) __attribute__((always_inline)) static inline void usb2_out_transfer_handler(uint8_t endp_num) { + vuint16_t num_bytes_received = R16_USB_RX_LEN; // this register seems to change during interrupts, save it volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.rx[endp_num]; vuint8_t* RX_CTRL = usb2_get_rx_endpoint_ctrl_reg(endp_num); @@ -664,14 +678,9 @@ usb2_out_transfer_handler(uint8_t endp_num) return; #endif - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "R8_UEP1_TX_CTRL "); - // LOG_8_BITS_REGISTER((uint8_t*)&R8_UEP1_TX_CTRL); if (endp->buffer != NULL) { - LOG_IF(LOG_LEVEL_DEBUG, LOG_ID_USB2, "Received %d bytes on ep %d\r\n", - R16_USB_RX_LEN, endp_num); - endp->state = usb2_backend_current_device->endpoints.rx_callback[endp_num](endp->buffer, R16_USB_RX_LEN); - + endp->state = usb2_backend_current_device->endpoints.rx_callback[endp_num](endp->buffer, num_bytes_received); *usb2_get_rx_endpoint_addr_reg(endp_num) = (uint32_t)endp->buffer; *RX_CTRL = (*RX_CTRL & ~RB_UEP_TRES_MASK) | endp->state; } @@ -686,9 +695,6 @@ void usb2_endp_rx_set_state_callback(uint8_t endp_num) { volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.rx[endp_num]; - if (endp == NULL) - return; - vuint8_t* RX_CTRL = usb2_get_rx_endpoint_ctrl_reg(endp_num); *RX_CTRL = (*RX_CTRL & ~RB_UEP_TRES_MASK) | endp->state; } @@ -697,9 +703,6 @@ void usb2_endp_tx_set_state_callback(uint8_t endp_num) { volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.tx[endp_num]; - if (endp == NULL) - return; - vuint8_t* TX_CTRL = usb2_get_tx_endpoint_ctrl_reg(endp_num); *TX_CTRL = (*TX_CTRL & ~RB_UEP_TRES_MASK) | endp->state; } @@ -721,7 +724,7 @@ void usb2_endp_tx_ready(uint8_t endp_num, uint16_t size) volatile USB_ENDPOINT* endp = &usb2_backend_current_device->endpoints.tx[endp_num]; vuint8_t* TX_CTRL = usb2_get_tx_endpoint_ctrl_reg(endp_num); vuint16_t* T_Len = usb2_get_tx_endpoint_len_reg(endp_num); - vuint16_t* tx_remaining_bytes = usb2_get_tx_endpoint_remaining_len(endp_num); + vuint16_t* tx_remaining_bytes = &endp_tx_remaining_bytes[endp_num]; #ifdef DEBUG if (endp == NULL || TX_CTRL == NULL || T_Len == NULL || @@ -731,12 +734,12 @@ void usb2_endp_tx_ready(uint8_t endp_num, uint16_t size) if (*tx_remaining_bytes > 0) { - LOG_IF( - LOG_LEVEL_DEBUG, LOG_ID_USB2, - "WARNING : endp has not finished transferring its current buffer \r\n"); + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "WARNING : endp has not finished transferring its current buffer \r\n"); } *tx_remaining_bytes = size; - *usb2_get_tx_endpoint_addr_reg(endp_num) = (uint32_t)endp->buffer; + + if (endp_num != 0) + *usb2_get_tx_endpoint_addr_reg(endp_num) = (uint32_t)endp->buffer; *T_Len = *tx_remaining_bytes > endp->max_packet_size ? endp->max_packet_size : *tx_remaining_bytes; *TX_CTRL = (*TX_CTRL & ~RB_UEP_TRES_MASK) | @@ -781,12 +784,14 @@ __attribute__((interrupt("WCH-Interrupt-fast"))) void USBHS_IRQHandler(void) else if ((R8_USB_INT_FG & RB_USB_IF_TRANSFER) && usb2_backend_current_device->state != POWERED) { -#ifdef LOG if (!(R8_USB_INT_ST & RB_USB_ST_TOGOK)) { - // LOG(" TOG MATCH FAIL : ENDP %x pid %x \n", usb_dev_endp, usb_pid); + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, " TOG MATCH FAIL : ENDP %x pid %x \n", usb_dev_endp, usb_pid); + // what to do here ? + // R8_USB_INT_FG = RB_USB_IF_TRANSFER; // Clear interrupt flag + // return; } -#endif + switch (usb_dev_endp) { case 0: diff --git a/src/wch-ch56x-lib/USBDevice/usb_device.h b/src/wch-ch56x-lib/USBDevice/usb_device.h index 863e9f5..c0eb129 100644 --- a/src/wch-ch56x-lib/USBDevice/usb_device.h +++ b/src/wch-ch56x-lib/USBDevice/usb_device.h @@ -35,6 +35,16 @@ USB3.0/USB2.0 management in the back. #ifndef USB_DEVICE_H #define USB_DEVICE_H +// Disable warnings in bsp arising from -pedantic -Wall -Wconversion +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#include "CH56x_common.h" +#include "CH56xSFR.h" +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop #include "wch-ch56x-lib/USBDevice/usb_descriptors.h" #include "wch-ch56x-lib/USBDevice/usb_endpoints.h" #include "wch-ch56x-lib/USBDevice/usb_types.h" @@ -126,15 +136,23 @@ void usb_device_set_endpoint_mask(usb_device_t* usb_device, uint32_t endpoint_ma __attribute__((always_inline)) inline static bool endp_tx_set_new_buffer(usb_device_t* usb_device, uint8_t endp_num, uint8_t* const ptr, uint16_t size) { + bsp_disable_interrupt(); volatile USB_ENDPOINT* ep = &usb_device->endpoints.tx[endp_num]; - if (size > ep->max_packet_size_with_burst || ptr == 0) { + bsp_enable_interrupt(); return false; } ep->buffer = ptr; + if (endp_num == 0) + { + if (usb_device->endpoints.rx[0].buffer == NULL) return false; + memcpy(usb_device->endpoints.rx[0].buffer, ptr, size); + } + + bsp_enable_interrupt(); if (usb_device->speed == USB30_SUPERSPEED) usb3_endpoints_backend_handled.usb3_endp_tx_ready(endp_num, size); else From 72a0ab9ff9748fe58f717a66bed068b3ec551293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Fuchs?= Date: Tue, 4 Jun 2024 13:52:27 +0200 Subject: [PATCH 10/10] Add stress test firmware to support Facedancer stress test --- tests/CMakeLists.txt | 3 +- .../test_firmware_usb_stress_test/.gitignore | 57 +++ tests/test_firmware_usb_stress_test/.ld | 1 + .../CMakeLists.txt | 101 ++++++ .../User/definitions.h | 32 ++ .../test_firmware_usb_stress_test/User/main.c | 324 ++++++++++++++++++ .../User/usb2_device_descriptors.h | 107 ++++++ .../User/usb_device.h | 209 +++++++++++ tests/test_firmware_usb_stress_test/format.sh | 3 + 9 files changed, 836 insertions(+), 1 deletion(-) create mode 100644 tests/test_firmware_usb_stress_test/.gitignore create mode 100644 tests/test_firmware_usb_stress_test/.ld create mode 100644 tests/test_firmware_usb_stress_test/CMakeLists.txt create mode 100644 tests/test_firmware_usb_stress_test/User/definitions.h create mode 100644 tests/test_firmware_usb_stress_test/User/main.c create mode 100644 tests/test_firmware_usb_stress_test/User/usb2_device_descriptors.h create mode 100644 tests/test_firmware_usb_stress_test/User/usb_device.h create mode 100644 tests/test_firmware_usb_stress_test/format.sh diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9c02f58..93e61bd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,5 +5,6 @@ add_subdirectory(test_firmware_serdes) add_subdirectory(test_firmware_unittests) add_subdirectory(test_firmware_usb_loopback) add_subdirectory(test_firmware_usb_speedtest) -add_subdirectory(test_firmware_fifo) add_subdirectory(test_firmware_usb_loopback_separate_usb_stacks) +add_subdirectory(test_firmware_usb_stress_test) +add_subdirectory(test_firmware_fifo) diff --git a/tests/test_firmware_usb_stress_test/.gitignore b/tests/test_firmware_usb_stress_test/.gitignore new file mode 100644 index 0000000..0470403 --- /dev/null +++ b/tests/test_firmware_usb_stress_test/.gitignore @@ -0,0 +1,57 @@ +# Prerequisites +*.d + +# astyle generated +*.*.orig + +# Object files +*.o +*.ko +*.obj +*.elf +*.bin +*.lst + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/tests/test_firmware_usb_stress_test/.ld b/tests/test_firmware_usb_stress_test/.ld new file mode 100644 index 0000000..12f10dd --- /dev/null +++ b/tests/test_firmware_usb_stress_test/.ld @@ -0,0 +1 @@ +/* bvernoux 18June2022 => Changed SECTION ".DMADATA :" to ".DMADATA (NOLOAD) :" => Added in section ".DMADATA" => *(.DMADATA*) => To have a correct _dmadata_end (as before _dmadata_start was always equal to _dmadata_end) */ ENTRY( _start ) __stack_size = 2048; PROVIDE( _stack_size = __stack_size ); MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 448K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16K RAMX (xrw) : ORIGIN = 0x20020000, LENGTH = 96K } 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*) *(.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 : { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin.o(.ctors)) KEEP (*crtbegin?.o(.ctors)) /* We don't want to include the .ctor section from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ 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 = . ); .DMADATA (NOLOAD) : { . = ALIGN(16); PROVIDE( _dmadata_start = .); *(.dmadata*) *(.dmadata.*) *(.DMADATA*) . = ALIGN(16); PROVIDE( _dmadata_end = .); } >RAMX AT>FLASH /**/ .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : { . = ALIGN(4); PROVIDE(_susrstack = . ); . = . + __stack_size; PROVIDE( _eusrstack = .); } >RAM } \ No newline at end of file diff --git a/tests/test_firmware_usb_stress_test/CMakeLists.txt b/tests/test_firmware_usb_stress_test/CMakeLists.txt new file mode 100644 index 0000000..a9f9297 --- /dev/null +++ b/tests/test_firmware_usb_stress_test/CMakeLists.txt @@ -0,0 +1,101 @@ +project(test_firmware_usb_stress_test LANGUAGES C) +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") + +add_executable(${PROJECT_NAME}) + +target_sources(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/User/main.c + ) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/User) + +##### Define program options + +#### wch-ch56x-lib options + +target_compile_definitions(wch-ch56x-lib-scheduled INTERFACE POOL_BLOCK_SIZE=512 POOL_BLOCK_NUM=40 INTERRUPT_QUEUE_SIZE=20) + +#### logging options + +# set(LOG_OUTPUT "printf") +# set(LOG_LEVEL 1) +# set(LOG_FILTER_IDS "1") + +if (DEFINED LOG_OUTPUT) + if (${LOG_OUTPUT} STREQUAL "buffer") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_BUFFER=1) + endif() + if (${LOG_OUTPUT} STREQUAL "serdes") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_SERDES=1) + endif() + if (${LOG_OUTPUT} STREQUAL "printf") + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_TYPE_PRINTF=1) + endif() +endif() + +if (DEFINED LOG_LEVEL) + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_LEVEL=${LOG_LEVEL}) +endif() + +if (DEFINED LOG_FILTER_IDS) + target_compile_definitions(${PROJECT_NAME} PRIVATE LOG_FILTER_IDS=${LOG_FILTER_IDS}) +endif() + +if (DEFINED STATIC_ANALYSIS) + target_compile_options(${PROJECT_NAME} PRIVATE -fanalyzer) +endif() + +##### Compilation and linkage options + +target_compile_options(${PROJECT_NAME} PRIVATE + -Werror -Warray-bounds=2 -Wno-comment -pedantic -Wall -Wno-error=unused-parameter + -Wbad-function-cast -Wredundant-decls -Wmissing-prototypes -Wchar-subscripts -Wshadow -Wundef -Wwrite-strings -Wunused -Wuninitialized -Wpointer-arith -Winline -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wmissing-declarations -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits -Wcast-align -Wswitch-enum + -Wextra -Wclobbered -Wcast-function-type -Wimplicit-fallthrough=3 -Wmissing-parameter-type -Wold-style-declaration -Woverride-init -Wshift-negative-value -Wunused-but-set-parameter +) + +if (DEFINED EXTRACFLAGS) +target_compile_options(${PROJECT_NAME} PRIVATE + -Wunused-parameter -Wno-error=unused-parameter + -Wsign-compare -Wno-error=sign-compare + -Wconversion -Wno-error=conversion -Wno-error=sign-conversion -Wno-error=float-conversion +) +endif() + +if (${RISCV_GCC_TOOLCHAIN_PREFIX} STREQUAL "riscv-none-embed-gcc") + message("Using riscv-none-embed-gcc") + target_compile_options(${PROJECT_NAME} PRIVATE -march=rv32imac) +elseif(${RISCV_GCC_TOOLCHAIN_PREFIX} STREQUAL "riscv-none-elf-gcc") + message("Using riscv-none-elf-gcc") + target_compile_options(${PROJECT_NAME} PRIVATE -march=rv32imac_zicsr) +else() + message("Toolchain not found") +endif() + +target_compile_options(${PROJECT_NAME} PRIVATE -std=gnu99 -MMD -MP -mno-strict-align -mabi=ilp32 -msmall-data-limit=8 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections) +target_compile_options(${PROJECT_NAME} PRIVATE $<$:-Og> $<$:-Oz>) +target_link_options(${PROJECT_NAME} PRIVATE -T "${CMAKE_CURRENT_LIST_DIR}/.ld" -nostartfiles LINKER:--gc-sections LINKER:--print-memory-usage -Wl,-Map,${PROJECT_NAME}.map --specs=nano.specs --specs=nosys.specs) +target_link_libraries(${PROJECT_NAME} wch-ch56x-lib-scheduled) + +##### Generate additional targets + +add_custom_target(${PROJECT_NAME}.bin ALL DEPENDS ${PROJECT_NAME}.elf) +add_custom_target(${PROJECT_NAME}.hex ALL DEPENDS ${PROJECT_NAME}.elf) +add_custom_target(${PROJECT_NAME}.lst ALL DEPENDS ${PROJECT_NAME}.elf) + +add_custom_command(TARGET ${PROJECT_NAME}.bin + COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf + ${PROJECT_NAME}.bin) + +add_custom_command(TARGET ${PROJECT_NAME}.hex + COMMAND ${CMAKE_OBJCOPY} -O ihex ${PROJECT_NAME}.elf + ${PROJECT_NAME}.hex) + +add_custom_command(TARGET ${PROJECT_NAME}.lst + COMMAND ${CMAKE_OBJDUMP} --source --all-headers --demangle --line-numbers --wide ${PROJECT_NAME}.elf > ${PROJECT_NAME}.lst) + +##### Export generated files + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.bin DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.hex DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.elf DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.lst DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.map DESTINATION ${CMAKE_SOURCE_DIR}/out/${PROJECT_NAME}) diff --git a/tests/test_firmware_usb_stress_test/User/definitions.h b/tests/test_firmware_usb_stress_test/User/definitions.h new file mode 100644 index 0000000..cef60e1 --- /dev/null +++ b/tests/test_firmware_usb_stress_test/User/definitions.h @@ -0,0 +1,32 @@ +/********************************** (C) COPYRIGHT ******************************* +Copyright (c) 2023 Quarkslab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*******************************************************************************/ + +#ifndef DEFINITIONS_H +#define DEFINITIONS_H + +#define ENDP_1_15_MAX_PACKET_SIZE 1024 +/* Global define */ +// DEF_ENDP_OUT_BURST_LEVEL / DEF_ENDP_IN_BURST_LEVEL maximum burst size 16 +// defined by the USB3 specification Warning USB3 endpoint bulk with 8 or 16 +// burst can be problematic on some PC +#define DEF_ENDP_OUT_BURST_LEVEL 4 +#define DEF_ENDP_IN_BURST_LEVEL (DEF_ENDP_OUT_BURST_LEVEL) +#define DEF_ENDP_MAX_SIZE (DEF_ENDP1_OUT_BURST_LEVEL * 1024) + +#define MAX_TRANSFER_LENGTH 768 + +#endif \ No newline at end of file diff --git a/tests/test_firmware_usb_stress_test/User/main.c b/tests/test_firmware_usb_stress_test/User/main.c new file mode 100644 index 0000000..6b27dd8 --- /dev/null +++ b/tests/test_firmware_usb_stress_test/User/main.c @@ -0,0 +1,324 @@ +/********************************** (C) COPYRIGHT ******************************* +Copyright (c) 2023 Quarkslab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*******************************************************************************/ + +// Disable warnings in bsp arising from -pedantic -Wall -Wconversion +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#pragma GCC diagnostic ignored "-Wvariadic-macros" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#include "CH56x_common.h" +#include "CH56xSFR.h" +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop + +#include "usb2_device_descriptors.h" +#include "usb_device.h" +#include "wch-ch56x-lib/logging/logging.h" +#include "wch-ch56x-lib/USBDevice/usb20.h" +#include "wch-ch56x-lib/USBDevice/usb_descriptors.h" +#include "wch-ch56x-lib/USBDevice/usb_endpoints.h" + +#undef FREQ_SYS +/* System clock / MCU frequency in Hz (lowest possible speed 15MHz) */ +#define FREQ_SYS (120000000) + +#define MIN(a, b) (a <= b ? a : b) + +#define EP_IN 2 + +volatile bool nak_received = false; +volatile uint16_t in_transfer_length = 32; +volatile uint16_t in_transfer_total_sent = 0; +volatile uint16_t in_transfer_last_sent = 0; +volatile uint16_t out_total_sent = 0; +volatile uint16_t last_out_total_sent = 0; + +uint8_t last_out_buffer[ENDP_1_15_MAX_PACKET_SIZE * DEF_ENDP_OUT_BURST_LEVEL] + __attribute__((section(".DMADATA"))); +uint8_t temp_in_buffer[ENDP_1_15_MAX_PACKET_SIZE * DEF_ENDP_OUT_BURST_LEVEL] + __attribute__((section(".DMADATA"))); + +void generate_data(uint8_t* buffer, uint16_t length); +void generate_data(uint8_t* buffer, uint16_t length) +{ + for (uint16_t i = 0; i < length; ++i) + { + buffer[i] = i % 256; + } +} + +void endp2_tx_complete(TRANSACTION_STATUS status); +void endp2_tx_complete(TRANSACTION_STATUS status) +{ + nak_received = false; + in_transfer_total_sent += in_transfer_last_sent; +} + +uint8_t endp1_rx_callback(uint8_t* const ptr, uint16_t size); +uint8_t endp1_rx_callback(uint8_t* const ptr, uint16_t size) +{ + LOG("writing %d bytes %x %x %x %x \r\n", size, endp0_buffer[0], endp0_buffer[1], endp0_buffer[2], endp0_buffer[3]); + memcpy(last_out_buffer + out_total_sent, ptr, size); + out_total_sent += size; + last_out_total_sent = out_total_sent; //because no ZLP is sent for bulk OUT ... meaning we can't detect the end of the transfer + if (size < usb_device_0.endpoints.rx[1].max_packet_size || size == 0) + { + out_total_sent = 0; + } + return 0x00; +} + +void nak_callback(uint8_t endp_num); +void nak_callback(uint8_t endp_num) +{ + if (!nak_received && endp_num == EP_IN) + { + LOG("nak received \r\n"); + if (in_transfer_total_sent == in_transfer_length) + { + in_transfer_total_sent = 0; + } + nak_received = true; + in_transfer_last_sent = MIN(in_transfer_length - in_transfer_total_sent, usb_device_0.endpoints.tx[EP_IN].max_packet_size_with_burst); + memcpy(endp2_tx_buffer, temp_in_buffer + in_transfer_total_sent, in_transfer_last_sent); + endp_tx_set_new_buffer(&usb_device_0, EP_IN, endp2_tx_buffer, in_transfer_last_sent); + LOG("end nak received"); + } +} + +/* Blink time in ms */ +#define BLINK_FAST (50) // Blink LED each 100ms (50*2) +#define BLINK_USB3 (250) // Blink LED each 500ms (250*2) +#define BLINK_USB2 (1000) // Blink LED each 2000ms (1000*2) +int blink_ms = BLINK_USB2; +USB_DEVICE_SPEED usb_device_old_speed = -1; + +uint16_t endp0_user_handled_control_request(USB_SETUP* request, + uint8_t** buffer); +uint16_t endp0_user_handled_control_request(USB_SETUP* request, + uint8_t** buffer) +{ + if (request->bRequest == 10) + { + LOG("writing %x %x %x %x \r\n", endp0_buffer[0], endp0_buffer[1], endp0_buffer[2], endp0_buffer[3]); + memcpy(last_out_buffer, endp0_buffer, request->wLength); + last_out_total_sent = request->wLength; + return 0x00; + } + else if (request->bRequest == 20) + { + in_transfer_length = request->wValue.bw.bb1 | (request->wIndex.bw.bb1 << 8); + LOG("requested %d bytes \r\n", in_transfer_length); + generate_data(usb2_backend_current_device->endpoints.rx[0].buffer, in_transfer_length); + *buffer = usb2_backend_current_device->endpoints.rx[0].buffer; + return in_transfer_length; + } + else if (request->bRequest == 1) + { + in_transfer_length = request->wValue.bw.bb1 | (request->wIndex.bw.bb1 << 8); + LOG("in transfer length %d \r\n", in_transfer_length); + generate_data(temp_in_buffer, in_transfer_length); + in_transfer_total_sent = 0; + in_transfer_last_sent = 0; + return 0x00; + } + else if (request->bRequest == 2) + { + *buffer = last_out_buffer; + LOG("returning last out buffer %d\r\n", last_out_total_sent); + return last_out_total_sent; + } + else if (request->bRequest == 3) + { + LOG("reset out buffer head\r\n"); + out_total_sent = 0; + return 0x00; + } + return 0xffff; +} + +void usb2_device_handle_bus_reset(void); +void usb2_device_handle_bus_reset(void) +{ + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "bus reset \r\n"); +} +/********************************************************************* + * @fn main + * + * @brief Main program. + * + * @return none + */ +int main() +{ + // Initialize board + bsp_gpio_init(); + bsp_init(FREQ_SYS); + + LOG_INIT(FREQ_SYS); + LOG("USB stress test azeaez\r\n"); + + usb_device_0.endpoints.endp0_user_handled_control_request = endp0_user_handled_control_request; + usb_device_0.endpoints.tx_complete[2] = endp2_tx_complete; + usb_device_0.endpoints.rx_callback[1] = endp1_rx_callback; + usb_device_0.endpoints.nak_callback = nak_callback; + + usb2_user_handled.usb2_device_handle_bus_reset = + &usb2_device_handle_bus_reset; + + // Finish initializing the descriptor parameters + init_usb2_descriptors(); + init_string_descriptors(); + + // Set the USB device parameters + usb_device_set_usb2_device_descriptor(&usb_device_0, &usb2_descriptors.usb_device_descr); + usb_device_set_usb2_config_descriptors(&usb_device_0, usb2_device_configs); + usb_device_set_string_descriptors(&usb_device_0, device_string_descriptors); + usb_device_set_endpoint_mask(&usb_device_0, ENDPOINT_1_RX | ENDPOINT_2_TX); + + init_endpoints(); + + usb_device_0.speed = USB2_HIGHSPEED; + usb2_device_init(); + usb2_enable_nak(true); + + // Infinite loop USB2/USB3 managed with Interrupt + while (1) + { + if (bsp_ubtn()) + { + blink_ms = BLINK_FAST; + bsp_uled_on(); + bsp_wait_ms_delay(blink_ms); + bsp_uled_off(); + bsp_wait_ms_delay(blink_ms); + LOG_DUMP(); + } + else + { + if (usb_device_0.state == CONFIGURED) + { + switch (usb_device_0.speed) + { + case USB2_LOWSPEED: // USB2 + case USB2_FULLSPEED: + case USB2_HIGHSPEED: { + if (usb_device_0.speed != usb_device_old_speed) + { + usb_device_old_speed = usb_device_0.speed; + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "USB2\n"); + } + blink_ms = BLINK_USB2; + bsp_uled_on(); + bsp_wait_ms_delay(blink_ms); + bsp_uled_off(); + bsp_wait_ms_delay(blink_ms); + } + break; + case USB30_SUPERSPEED: // USB3 + { + if (usb_device_0.speed != usb_device_old_speed) + { + usb_device_old_speed = usb_device_0.speed; + LOG_IF_LEVEL(LOG_LEVEL_DEBUG, "USB3\n"); + } + blink_ms = BLINK_USB3; + bsp_uled_on(); + bsp_wait_ms_delay(blink_ms); + bsp_uled_off(); + bsp_wait_ms_delay(blink_ms); + } + break; + case SPEED_NONE: + default: + bsp_uled_on(); // LED is steady until USB3 SS or USB2 HS is ready + break; + } + } + else + { + bsp_uled_on(); // LED is steady until USB3 SS or USB2 HS is ready + } + } + } +} + +__attribute__((interrupt("WCH-Interrupt-fast"))) void WDOG_IRQHandler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void WDOG_IRQHandler(void) +{ + LOG_DUMP(); + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, + "WDOG_IRQHandler\r\n" + " SP=0x%08X\r\n" + " MIE=0x%08X\r\n" + " MSTATUS=0x%08X\r\n" + " MCAUSE=0x%08X\r\n" + " MVENDORID=0x%08X\r\n" + " MARCHID=0x%08X\r\n" + " MISA=0x%08X\r\n" + " MIMPID=0x%08X\r\n" + " MHARTID=0x%08X\r\n" + " MEPC=0x%08X\r\n" + " MSCRATCH=0x%08X\r\n" + " MTVEC=0x%08X\r\n" + " MTVAL=0x%08X\r\n", + __get_SP(), __get_MIE(), __get_MSTATUS(), __get_MCAUSE(), + __get_MVENDORID(), __get_MARCHID(), __get_MISA(), __get_MIMPID(), + __get_MHARTID(), __get_MEPC(), __get_MSCRATCH(), __get_MTVEC(), __get_MTVAL()); + + LOG_DUMP(); + + bsp_wait_ms_delay(100000000); +} + +/********************************************************************* + * @fn HardFault_Handler + * + * @brief Example of basic HardFault Handler called if an exception occurs + * + * @return none + */ +__attribute__((interrupt("WCH-Interrupt-fast"))) void HardFault_Handler(void); +__attribute__((interrupt("WCH-Interrupt-fast"))) void HardFault_Handler(void) +{ + LOG_DUMP(); + LOG("this might be the return addr of current function %x \r\n", __builtin_return_address(0)); + // asm("ebreak"); to trigger a breakpoint and test hardfault_handler + LOG_IF_LEVEL(LOG_LEVEL_CRITICAL, + "HardFault_Handler\r\n" + " SP=0x%08X\r\n" + " MIE=0x%08X\r\n" + " MSTATUS=0x%08X\r\n" + " MCAUSE=0x%08X\r\n" + " MVENDORID=0x%08X\r\n" + " MARCHID=0x%08X\r\n" + " MISA=0x%08X\r\n" + " MIMPID=0x%08X\r\n" + " MHARTID=0x%08X\r\n" + " MEPC=0x%08X\r\n" + " MSCRATCH=0x%08X\r\n" + " MTVEC=0x%08X\r\n" + " MTVAL=0x%08X\r\n", + __get_SP(), __get_MIE(), __get_MSTATUS(), __get_MCAUSE(), + __get_MVENDORID(), __get_MARCHID(), __get_MISA(), __get_MIMPID(), + __get_MHARTID(), __get_MEPC(), __get_MSCRATCH(), __get_MTVEC(), __get_MTVAL()); + + LOG_DUMP(); + + bsp_wait_ms_delay(100000000); +} diff --git a/tests/test_firmware_usb_stress_test/User/usb2_device_descriptors.h b/tests/test_firmware_usb_stress_test/User/usb2_device_descriptors.h new file mode 100644 index 0000000..140f204 --- /dev/null +++ b/tests/test_firmware_usb_stress_test/User/usb2_device_descriptors.h @@ -0,0 +1,107 @@ +/********************************** (C) COPYRIGHT ******************************* +Copyright (c) 2023 Quarkslab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*******************************************************************************/ + +#ifndef USB2_DEVICE_DESCRIPTOR_H +#define USB2_DEVICE_DESCRIPTOR_H + +#include "definitions.h" +#include "wch-ch56x-lib/USBDevice/usb_descriptors.h" + +const uint8_t* usb2_device_configs[1]; + +struct usb2_descriptors +{ + USB_DEV_DESCR usb_device_descr; + struct __PACKED + { + USB_CFG_DESCR usb_cfg_descr; + USB_ITF_DESCR usb_itf_descr; + USB_ENDP_DESCR usb_endp_descr_1; + USB_ENDP_DESCR usb_endp_descr_2_tx; + } other_descr; +} usb2_descriptors; + +void init_usb2_descriptors(void); + +void init_usb2_descriptors(void) +{ + usb2_descriptors.usb_device_descr = (USB_DEV_DESCR){ + .bLength = 0x12, + .bDescriptorType = 0x01, // device descriptor type + .bcdUSB = 0x0200, // usb2.0 + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 64, + .bcdDevice = 0x0001, + .idVendor = + 0x1209, // https://github.com/obdev/v-usb/blob/master/usbdrv/usb-ids-for-free.txt + .idProduct = 0x0001, + .iProduct = 0x01, + .iManufacturer = 0x00, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01 + }; + + usb2_descriptors.other_descr.usb_cfg_descr = (USB_CFG_DESCR){ + .bLength = 0x09, + .bDescriptorType = 0x02, + .wTotalLength = sizeof(usb2_descriptors.other_descr), + .bNumInterfaces = 0x01, + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xa0, // supports remote wake-up + .MaxPower = 0x64 // 200ma + }; + + usb2_descriptors.other_descr.usb_itf_descr = + (USB_ITF_DESCR){ .bLength = 0x09, + .bDescriptorType = 0x04, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = 0xff, // vendor-specific + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .iInterface = 0x00 }; + + usb2_descriptors.other_descr.usb_endp_descr_1 = (USB_ENDP_DESCR){ + .bLength = 0x07, + .bDescriptorType = 0x05, + .bEndpointAddress = (ENDPOINT_DESCRIPTOR_ADDRESS_OUT | 0x01) & + ENDPOINT_DESCRIPTOR_ADDRESS_MASK, + .bmAttributes = ENDPOINT_DESCRIPTOR_BULK_TRANSFER, + .wMaxPacketSizeL = 0x00, + .wMaxPacketSizeH = 0x02, // 512 bytes + .bInterval = 255 // max NAK rate + }; + + usb2_descriptors.other_descr.usb_endp_descr_2_tx = (USB_ENDP_DESCR){ + .bLength = 0x07, + .bDescriptorType = 0x05, + .bEndpointAddress = (ENDPOINT_DESCRIPTOR_ADDRESS_IN | 0x02) & + ENDPOINT_DESCRIPTOR_ADDRESS_MASK, + .bmAttributes = ENDPOINT_DESCRIPTOR_BULK_TRANSFER, + .wMaxPacketSizeL = 0x00, + .wMaxPacketSizeH = 0x02, // 512 bytes + .bInterval = 0 + }; + + usb2_device_configs[0] = (uint8_t*)&usb2_descriptors.other_descr; +} + +#endif diff --git a/tests/test_firmware_usb_stress_test/User/usb_device.h b/tests/test_firmware_usb_stress_test/User/usb_device.h new file mode 100644 index 0000000..2d9efce --- /dev/null +++ b/tests/test_firmware_usb_stress_test/User/usb_device.h @@ -0,0 +1,209 @@ +/********************************** (C) COPYRIGHT ******************************* +Copyright (c) 2023 Quarkslab + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*******************************************************************************/ + +#ifndef USB_DEVICE_USER_H +#define USB_DEVICE_USER_H + +#include "definitions.h" +#include "wch-ch56x-lib/USBDevice/usb_descriptors.h" +#include "wch-ch56x-lib/USBDevice/usb_device.h" +#include "wch-ch56x-lib/USBDevice/usb_endpoints.h" + +uint8_t hydradancer_product_string_descriptor[] = { + 'H', + 0x00, + 'y', + 0x00, + 'd', + 0x00, + 'r', + 0x00, + 'a', + 0x00, + 'd', + 0x00, + 'a', + 0x00, + 'n', + 0x00, + 'c', + 0x00, + 'e', + 0x00, + 'r', + 0x00, + ' ', + 0x00, + 'T', + 0x00, + 'e', + 0x00, + 's', + 0x00, + 't', + 0x00, + ' ', + 0x00, + 'F', + 0x00, + 'i', + 0x00, + 'r', + 0x00, + 'm', + 0x00, + 'w', + 0x00, + 'a', + 0x00, + 'r', + 0x00, + 'e', + 0x00, + ',', + 0x00, + ' ', + 0x00, + 'b', + 0x00, + 'i', + 0x00, + 'd', + 0x00, + 'i', + 0x00, + 'r', + 0x00, + 'e', + 0x00, + 'c', + 0x00, + 't', + 0x00, + 'i', + 0x00, + 'o', + 0x00, + 'n', + 0x00, + 'n', + 0x00, + 'a', + 0x00, + 'l', + 0x00, + ',', + 0x00, + ' ', + 0x00, + 'a', + 0x00, + 'l', + 0x00, + 'l', + 0x00, + ' ', + 0x00, + 'e', + 0x00, + 'n', + 0x00, + 'd', + 0x00, + 'p', + 0x00, + 'o', + 0x00, + 'i', + 0x00, + 'n', + 0x00, + 't', + 0x00, + 's', + 0x00, +}; + +struct usb_string_descriptors +{ + USB_STRING_DESCR lang_ids_descriptor; + uint16_t lang_ids[1]; + USB_STRING_DESCR product_string_descriptor; + uint8_t hydradancer_product_string_descriptor[sizeof( + hydradancer_product_string_descriptor)]; +} usb_string_descriptors; + +const USB_STRING_DESCR* device_string_descriptors[2]; + +__attribute__((aligned(16))) +uint8_t endp0_buffer[MAX_TRANSFER_LENGTH] + __attribute__((section(".DMADATA"))); +__attribute__((aligned(16))) + +uint8_t endp1_rx_buffer[ENDP_1_15_MAX_PACKET_SIZE * DEF_ENDP_OUT_BURST_LEVEL] + __attribute__((section(".DMADATA"))); + +__attribute__((aligned(16))) +uint8_t endp2_tx_buffer[ENDP_1_15_MAX_PACKET_SIZE * DEF_ENDP_IN_BURST_LEVEL] + __attribute__((section(".DMADATA"))); + +void init_string_descriptors(void); +void init_string_descriptors(void) +{ + usb_string_descriptors.lang_ids_descriptor = (USB_STRING_DESCR){ + .bLength = + sizeof(USB_STRING_DESCR) + sizeof(usb_string_descriptors.lang_ids), + .bDescriptorType = 0x03, // String Descriptor + }; + + usb_string_descriptors.lang_ids[0] = 0x0409; + + usb_string_descriptors.product_string_descriptor = (USB_STRING_DESCR){ + .bLength = sizeof(USB_STRING_DESCR) + + sizeof(hydradancer_product_string_descriptor), + .bDescriptorType = 0x03, // String Descriptor + }; + + memcpy(&usb_string_descriptors.hydradancer_product_string_descriptor, + hydradancer_product_string_descriptor, + sizeof(hydradancer_product_string_descriptor)); + + device_string_descriptors[0] = &usb_string_descriptors.lang_ids_descriptor; + device_string_descriptors[1] = + &usb_string_descriptors.product_string_descriptor; +} + +void init_endpoints(void); +void init_endpoints(void) +{ + usb_device_0.endpoints.rx[0].buffer = endp0_buffer; + usb_device_0.endpoints.rx[0].max_packet_size = 512; + usb_device_0.endpoints.rx[0].max_burst = 1; + usb_device_0.endpoints.rx[0].max_packet_size_with_burst = 512; + + usb_device_0.endpoints.rx[1].buffer = endp1_rx_buffer; + usb_device_0.endpoints.rx[1].max_packet_size = 512; + usb_device_0.endpoints.rx[1].max_burst = 4; + usb_device_0.endpoints.rx[1].max_packet_size_with_burst = 512; + + usb_device_0.endpoints.tx[2].buffer = NULL; + usb_device_0.endpoints.tx[2].max_packet_size = 512; + usb_device_0.endpoints.tx[2].max_burst = 4; + usb_device_0.endpoints.tx[2].max_packet_size_with_burst = 512; +} + +#endif diff --git a/tests/test_firmware_usb_stress_test/format.sh b/tests/test_firmware_usb_stress_test/format.sh new file mode 100644 index 0000000..6ec4ecb --- /dev/null +++ b/tests/test_firmware_usb_stress_test/format.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find ./User \( -iname "*.h" -o -iname "*.c" \) -print0 | xargs -0 clang-format --verbose --style=file -i;