Skip to content

Initial support for GD32C103 and GD32E103#413

Open
minh7a6 wants to merge 7 commits intocherry-embedded:masterfrom
minh7a6:feature/gd32c103_support
Open

Initial support for GD32C103 and GD32E103#413
minh7a6 wants to merge 7 commits intocherry-embedded:masterfrom
minh7a6:feature/gd32c103_support

Conversation

@minh7a6
Copy link
Copy Markdown

@minh7a6 minh7a6 commented Apr 26, 2026

Hi all,

I'm creating this PR to add initial support for GD32C103 and GD32E103. I've tested preliminarily for GD32C103 in device mode. Since GD32E103 is pretty much the same variant without CAN Bus, I expected this to work the same. Similarly to GD32VF103 port from TinyUSB, there are unknown reasons why hwcfg2 reads all 0. Hence I'm adding new functions to override this hw params.

Thanks

Summary by CodeRabbit

  • New Features
    • Added USB device support for GD32 ARM boards.
    • Introduced a customizable hardware-parameter override so platform code can adjust USB controller settings at init.
    • Provided board-level USB initialization/deinitialization and optional custom FIFO sizing.
    • Added simple system clock and delay helpers to aid USB timing on supported platforms.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 26, 2026

📝 Walkthrough

Walkthrough

Adds a weak extension hook for runtime DWC2 hardware-parameter overrides and a new GD32 ARM glue layer supplying platform-specific user/hw params, low-level init/deinit, FIFO customization, delay/clock helpers, and an endpoint-count override.

Changes

Cohort / File(s) Summary
DWC2 USB Driver Core
port/dwc2/usb_dc_dwc2.c
Adds a weakly-linked hook dwc2_override_hw_params(uint32_t reg_base, struct dwc2_hw_params *hw) and invokes it from usb_dc_init() after dwc2_get_hwparams()/dwc2_get_user_params() populate parameters. Default stub is a no-op.
GD32 ARM Glue Layer (new)
port/dwc2/usb_glue_gd_arm.c
New file: provides param_pa11_pa12 defaults (FS PHY, DMA disabled, RX/TX FIFO sizes, GCCFG bits), enforces CONFIG_USBDEV_EP_NUM == 4, implements usb_dc_low_level_init/deinit, supplies dwc2_get_user_params (unless custom), optional FIFO override (CONFIG_USB_DWC2_CUSTOM_FIFO), implements dwc2_override_hw_params to adjust hw->num_dev_ep for reg_base == 0x50000000UL, and adds usbd_dwc2_delay_ms and usbd_dwc2_get_system_clock helpers.

Sequence Diagram(s)

sequenceDiagram
    participant App as USB stack
    participant DWC2_Core as DWC2 core
    participant GD32_Glue as GD32 glue
    participant HW as USB hardware

    App->>DWC2_Core: usb_dc_init(busid)
    DWC2_Core->>DWC2_Core: dwc2_get_hwparams(reg_base, hw)
    DWC2_Core->>DWC2_Core: dwc2_get_user_params(reg_base, user) (if provided)
    DWC2_Core->>GD32_Glue: dwc2_override_hw_params(reg_base, hw) [weak hook]
    GD32_Glue-->>DWC2_Core: (optional) modify hw params
    DWC2_Core->>GD32_Glue: usb_dc_low_level_init(busid)
    GD32_Glue->>HW: enable clocks / IRQ
    DWC2_Core->>HW: initialize controller with hw/user params
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble at params, soft and neat, 🐇
A weak hook lets defaults and custom meet.
GD32 hums, FIFOs align,
IRQs wake at init time.
Hops of bytes — my tail’s a beat.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main changeset objective: adding initial support for two specific GD32 microcontroller variants (GD32C103 and GD32E103).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@minh7a6 minh7a6 changed the title Added initial support for GD32C103 and GD32E103 Initial support for GD32C103 and GD32E103 Apr 26, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
port/dwc2/usb_glue_gd_arm.c (1)

42-48: Silence the unused-parameter warning on busid.

Both helpers ignore busid, which trips -Wunused-parameter in strict build configurations. Add an explicit (void)busid; (matching the pattern used in the weak hook itself).

🛠️ Proposed fix
 void usb_dc_low_level_init(uint8_t busid) {
+  (void)busid;
   NVIC_EnableIRQ(USBFS_IRQn);
 }

 void usb_dc_low_level_deinit(uint8_t busid) {
+  (void)busid;
   NVIC_DisableIRQ(USBFS_IRQn);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 42 - 48, Both
usb_dc_low_level_init(uint8_t busid) and usb_dc_low_level_deinit(uint8_t busid)
ignore the busid parameter and trigger -Wunused-parameter; add an explicit
(void)busid; at the start of each function (in usb_dc_low_level_init and
usb_dc_low_level_deinit) to silence the warning and match the pattern used in
the weak hook implementations, keeping the rest of the functions unchanged.
port/dwc2/usb_dc_dwc2.c (1)

326-329: Consider relocating the weak hook for readability.

The weak dwc2_override_hw_params definition is awkwardly inserted between dwc2_ep_write and dwc2_ep_read. Functionally fine, but grouping it near the param-related helpers (or at the top of the file with other forward-declared helpers) keeps related code together.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_dc_dwc2.c` around lines 326 - 329, The weak hook
dwc2_override_hw_params is currently placed between dwc2_ep_write and
dwc2_ep_read which breaks logical grouping; move the __WEAK void
dwc2_override_hw_params(uint32_t reg_base, struct dwc2_hw_params *hw) stub so it
lives with other parameter-related helpers (or with the top section of
forward-declared helper hooks) to improve readability and maintainability—ensure
you remove the stub from between dwc2_ep_write and dwc2_ep_read and reinsert it
near the hw-param helper functions (or header/top helpers) preserving the same
signature and (void) casts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@port/dwc2/usb_glue_gd_arm.c`:
- Around line 6-10: Add a direct include for the C string header and switch the
system stdint include to angle-bracket form: explicitly include <string.h> (so
memcpy is guaranteed available and not relied on transitively via usbd_core.h)
and change "stdint.h" to use <stdint.h>; update the include list near the top of
usb_glue_gd_arm.c (look for the existing "stdint.h" and uses of memcpy) to make
these changes.
- Around line 66-70: The hard-coded assignment in dwc2_override_hw_params
(hw->num_dev_ep = 4) breaks boards that are built with CONFIG_USBDEV_EP_NUM ==
6; change the override to derive the number of device endpoints from the build
config instead of hard-coding 4 (e.g., set hw->num_dev_ep = CONFIG_USBDEV_EP_NUM
or a min/max guard using CONFIG_USBDEV_EP_NUM), and ensure this hw override
remains active even when CONFIG_USB_DWC2_CUSTOM_PARAM is defined (i.e., only
gate dwc2_get_user_params on CONFIG_USB_DWC2_CUSTOM_PARAM and keep
dwc2_override_hw_params unconditional) so usb_dc_init and usbd_ep_open will see
the correct endpoint count.
- Around line 12-36: Make the global param_pa11_pa12 static and either provide a
second params set for the 6‑EP layout or restrict this file to the 4‑EP variant:
mark the constant param_pa11_pa12 as static, then address device_tx_fifo_size
and total_fifo_size so they match the supported CONFIG_USBDEV_EP_NUM (for 6 EPs
allocate non‑zero entries for indices 0..5 and ensure total_fifo_size covers all
FIFOs), or update the `#error` block to declare only the pa11/pa12 (4‑EP) variant
is supported; also ensure usb_dc_init() will see the correct params selection
based on CONFIG_USBDEV_EP_NUM.
- Around line 72-78: The busy-wait in usbd_dwc2_delay_ms assumes one cycle per
iteration and is inaccurate and fragile; change the prototype to accept uint32_t
ms, and replace the loop with one of two options: (A) use a hardware timer
(preferably DWT cycle counter or SysTick) to compute exact delay based on
SystemCoreClock and busy-wait on the cycle count for precise timing, or (B) if
hardware timers are unavailable, calibrate the loop by measuring
cycles/iteration on the target and divide the computed count (e.g., count =
SystemCoreClock / 1000 / N * ms) so the loop matches real cycles; update all
callers (e.g., dwc2_set_mode and usbd_set_remote_wakeup) to pass uint32_t and
verify delays remain ~10 ms.

---

Nitpick comments:
In `@port/dwc2/usb_dc_dwc2.c`:
- Around line 326-329: The weak hook dwc2_override_hw_params is currently placed
between dwc2_ep_write and dwc2_ep_read which breaks logical grouping; move the
__WEAK void dwc2_override_hw_params(uint32_t reg_base, struct dwc2_hw_params
*hw) stub so it lives with other parameter-related helpers (or with the top
section of forward-declared helper hooks) to improve readability and
maintainability—ensure you remove the stub from between dwc2_ep_write and
dwc2_ep_read and reinsert it near the hw-param helper functions (or header/top
helpers) preserving the same signature and (void) casts.

In `@port/dwc2/usb_glue_gd_arm.c`:
- Around line 42-48: Both usb_dc_low_level_init(uint8_t busid) and
usb_dc_low_level_deinit(uint8_t busid) ignore the busid parameter and trigger
-Wunused-parameter; add an explicit (void)busid; at the start of each function
(in usb_dc_low_level_init and usb_dc_low_level_deinit) to silence the warning
and match the pattern used in the weak hook implementations, keeping the rest of
the functions unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f79a897-cf26-4988-9ab4-c5fdd301ba6e

📥 Commits

Reviewing files that changed from the base of the PR and between 792a50c and 79efe16.

📒 Files selected for processing (2)
  • port/dwc2/usb_dc_dwc2.c
  • port/dwc2/usb_glue_gd_arm.c

Comment on lines +6 to +10
#include "usbd_core.h"
#include "stdint.h"
#include "usb_dwc2_reg.h"
#include "usb_dwc2_param.h"
#include "board_config.h"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing <string.h> and prefer angle-bracket form for <stdint.h>.

memcpy is called at line 53 but <string.h> is not included anywhere in this file (it may transitively be available via usbd_core.h, but relying on that is fragile). Also, stdint.h is a system header and should use angle brackets, matching the rest of the project.

🛠️ Proposed fix
 `#include` "usbd_core.h"
-#include "stdint.h"
+#include <stdint.h>
+#include <string.h>
 `#include` "usb_dwc2_reg.h"
 `#include` "usb_dwc2_param.h"
 `#include` "board_config.h"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#include "usbd_core.h"
#include "stdint.h"
#include "usb_dwc2_reg.h"
#include "usb_dwc2_param.h"
#include "board_config.h"
`#include` "usbd_core.h"
`#include` <stdint.h>
`#include` <string.h>
`#include` "usb_dwc2_reg.h"
`#include` "usb_dwc2_param.h"
`#include` "board_config.h"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 6 - 10, Add a direct include for
the C string header and switch the system stdint include to angle-bracket form:
explicitly include <string.h> (so memcpy is guaranteed available and not relied
on transitively via usbd_core.h) and change "stdint.h" to use <stdint.h>; update
the include list near the top of usb_glue_gd_arm.c (look for the existing
"stdint.h" and uses of memcpy) to make these changes.

Comment thread port/dwc2/usb_glue_gd_arm.c Outdated
Comment thread port/dwc2/usb_glue_gd_arm.c Outdated
Comment on lines +72 to +78
void usbd_dwc2_delay_ms(uint8_t ms)
{
uint32_t count = SystemCoreClock / 1000 * ms;
while (count--) {
__asm volatile("nop");
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all callers of usbd_dwc2_delay_ms and the duration values they pass,
# to gauge timing sensitivity.
rg -nP '\busbd_dwc2_delay_ms\s*\(' -C1

Repository: cherry-embedded/CherryUSB

Length of output: 1818


🏁 Script executed:

#!/bin/bash
# Check implementations across multiple glue files to see if pattern is consistent
echo "=== GD ARM implementation ==="
sed -n '72,78p' port/dwc2/usb_glue_gd_arm.c

echo -e "\n=== GD implementation ==="
sed -n '44,50p' port/dwc2/usb_glue_gd.c

echo -e "\n=== ST implementation ==="
sed -n '717,723p' port/dwc2/usb_glue_st.c

echo -e "\n=== HC implementation ==="
sed -n '297,303p' port/dwc2/usb_glue_hc.c

Repository: cherry-embedded/CherryUSB

Length of output: 776


🏁 Script executed:

#!/bin/bash
# Search for broader context of the delay calls and related functions
echo "=== Context around delay calls in usb_dc_dwc2.c ==="
sed -n '125,145p' port/dwc2/usb_dc_dwc2.c

echo -e "\n=== Context around second delay call ==="
sed -n '660,670p' port/dwc2/usb_dc_dwc2.c

echo -e "\n=== Search for dwc2_set_mode and usbd_set_remote_wakeup mentioned in review ==="
rg -n 'dwc2_set_mode|usbd_set_remote_wakeup' port/dwc2/ -A2 -B2 | head -50

Repository: cherry-embedded/CherryUSB

Length of output: 2448


🏁 Script executed:

#!/bin/bash
# Check if there's SystemCoreClock definition or any other delay implementations
echo "=== Looking for other delay timing approaches in the codebase ==="
rg -n 'SysTick|DWT|SystemTick' port/dwc2/ | head -20
echo ""
echo "=== Checking if there are calibration constants or timing configs ==="
rg -n 'calibrat|timing|cycle' port/dwc2/ -i | head -20

Repository: cherry-embedded/CherryUSB

Length of output: 345


Loop-based delay is subject to significant cycle overhead.

The implementation assumes each loop iteration takes exactly one cycle, but the actual loop body (count--, compare, branch, plus volatile nop) typically consumes 3+ cycles on Cortex-M. This results in an actual delay roughly 3× longer than the calculation intends. For the current 10 ms calls (in dwc2_set_mode and usbd_set_remote_wakeup), this stretches the delay to ~30 ms or more, which is acceptable for USB mode switching but still inaccurate and fragile.

Recommend either:

  1. Use a SysTick- or DWT-cycle-counter-based delay for accurate timing.
  2. Calibrate the loop and divide the count, e.g. count = SystemCoreClock / 1000 / 4 * ms after measurement on the target.

Additionally, ms parameter is uint8_t, limiting the API to 255 ms. Consider widening to uint32_t for consistency with typical delay signatures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 72 - 78, The busy-wait in
usbd_dwc2_delay_ms assumes one cycle per iteration and is inaccurate and
fragile; change the prototype to accept uint32_t ms, and replace the loop with
one of two options: (A) use a hardware timer (preferably DWT cycle counter or
SysTick) to compute exact delay based on SystemCoreClock and busy-wait on the
cycle count for precise timing, or (B) if hardware timers are unavailable,
calibrate the loop by measuring cycles/iteration on the target and divide the
computed count (e.g., count = SystemCoreClock / 1000 / N * ms) so the loop
matches real cycles; update all callers (e.g., dwc2_set_mode and
usbd_set_remote_wakeup) to pass uint32_t and verify delays remain ~10 ms.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
port/dwc2/usb_glue_gd_arm.c (1)

42-48: Unused busid parameter.

busid is unused in both usb_dc_low_level_init and usb_dc_low_level_deinit. Add (void)busid; to silence -Wunused-parameter, matching other glue layers.

♻️ Proposed fix
 void usb_dc_low_level_init(uint8_t busid) {
+  (void)busid;
   NVIC_EnableIRQ(USBFS_IRQn);
 }

 void usb_dc_low_level_deinit(uint8_t busid) {
+  (void)busid;
   NVIC_DisableIRQ(USBFS_IRQn);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 42 - 48, The functions
usb_dc_low_level_init and usb_dc_low_level_deinit currently ignore their busid
parameter and trigger -Wunused-parameter; add a deliberate unused-parameter
suppression by inserting (void)busid; at the start of each function (inside
usb_dc_low_level_init and usb_dc_low_level_deinit) to match other glue layers
and silence the warning.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@port/dwc2/usb_glue_gd_arm.c`:
- Around line 67-73: In dwc2_override_hw_params update the hardcoded num_dev_ep
and magic address: set hw->num_dev_ep to CONFIG_USBDEV_EP_NUM - 1 (since
num_dev_ep is the highest zero-based EP index used by the core loops) instead of
4, and replace the literal 0x50000000UL with the platform constant (e.g.,
USBFS_BASE from your GD32 CMSIS headers included via board_config.h); if
USBFS_BASE may be missing on some variants, add a local conditional fallback
define for that platform constant and use it in the reg_base comparison.

---

Nitpick comments:
In `@port/dwc2/usb_glue_gd_arm.c`:
- Around line 42-48: The functions usb_dc_low_level_init and
usb_dc_low_level_deinit currently ignore their busid parameter and trigger
-Wunused-parameter; add a deliberate unused-parameter suppression by inserting
(void)busid; at the start of each function (inside usb_dc_low_level_init and
usb_dc_low_level_deinit) to match other glue layers and silence the warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cb8dfff-0cde-453f-8f7a-3c44ff25d299

📥 Commits

Reviewing files that changed from the base of the PR and between 79efe16 and 00048c8.

📒 Files selected for processing (1)
  • port/dwc2/usb_glue_gd_arm.c

Comment on lines +67 to +73
void dwc2_override_hw_params(uint32_t reg_base, struct dwc2_hw_params *hw) {
/* HWCFG2 reads 0, this is unknown why */
if(reg_base == 0x50000000UL) {
hw->num_dev_ep = 4;
}
/* TODO: For other GD32, potentially will need to update like this as well*/
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=c -C2 '\bnum_dev_ep\b' port/dwc2/usb_dc_dwc2.c
rg -nP --type=c -C1 '\bUSBD_BASE\b|\bUSBFS_BASE\b' port/dwc2/

Repository: cherry-embedded/CherryUSB

Length of output: 2750


🏁 Script executed:

#!/bin/bash
# Search for CONFIG_USBDEV_EP_NUM definition and usage
rg -n 'CONFIG_USBDEV_EP_NUM' --type=c --type=h

# Search for all definitions of USBD_BASE and USBFS_BASE
rg -n 'define\s+(USBD_BASE|USBFS_BASE)' --type=c --type=h

# Search for 0x50000000 to see where the magic literal appears
rg -n '0x50000000' --type=c --type=h

# Check GD32-related header includes
rg -n 'include.*gd32' --type=c --type=h -i | head -20

Repository: cherry-embedded/CherryUSB

Length of output: 5664


🏁 Script executed:

#!/bin/bash
# Search for GD32-related includes or USB definitions
fd -type f -name "*.h" | xargs grep -l "GD32\|gd32" 2>/dev/null | head -20

# Search for USB base defines in the codebase
rg -n 'define.*BASE.*0x50000000' --type=h --type=c

# Check what's in the GD files - look for the glue file itself and related headers
cat -n port/dwc2/usb_glue_gd_arm.c | head -75

# Look for includes in usb_glue_gd_arm.c
rg -n '#include' port/dwc2/usb_glue_gd_arm.c

Repository: cherry-embedded/CherryUSB

Length of output: 3084


🏁 Script executed:

#!/bin/bash
# Find board_config.h references
rg -l 'board_config.h' --type=c --type=h | head -10

# Search for USBFS or USB related defines in GD32 context
rg -n 'USBFS\|USBD' --type=h | grep -i gd32 | head -20

# Look for any GD32 CMakeLists or config files
fd -name 'CMakeLists.txt' -o -name '*.cmake' | xargs grep -l 'gd32\|GD32' 2>/dev/null

# Check examples directory structure for GD32
find . -type d -name '*gd*' 2>/dev/null | head -10

# Search for where USBFS_IRQn is defined (used in usb_glue_gd_arm.c)
rg -n 'USBFS_IRQn' --type=c --type=h

Repository: cherry-embedded/CherryUSB

Length of output: 493


🏁 Script executed:

#!/bin/bash
# Search for any GD32-related header files or documentation
find . -type f \( -name "*.h" -o -name "*.md" \) -exec grep -l "GD32\|0x50000000" {} \; | head -20

# Look at includes in the glue file more carefully
grep -E '#include|USBFS' port/dwc2/usb_glue_gd_arm.c

# Search for any constants that might map to 0x50000000
rg '0x50000000' -B2 -A2 --type=c --type=h

# Check the dwc2_override_hw_params function calls
rg -A5 -B5 'dwc2_override_hw_params' --type=c | head -40

Repository: cherry-embedded/CherryUSB

Length of output: 3329


Fix the off-by-one error in num_dev_ep and replace the magic literal with a platform constant.

Two critical issues require correction:

  1. Off-by-one on num_dev_ep. The core loops at usb_dc_dwc2.c:530, 602, 1156 iterate as i < (num_dev_ep + 1), confirming num_dev_ep is the highest zero-based EP index, not the count. With CONFIG_USBDEV_EP_NUM = 4 (EPs 0–3), num_dev_ep must be 3, not 4. Setting it to 4 causes 5 iterations, accessing device_tx_fifo_size[4] (which is 0), violating FIFO semantics. Use hw->num_dev_ep = CONFIG_USBDEV_EP_NUM - 1;.

  2. Magic literal 0x50000000UL. Replace with a GD32 platform constant (e.g., from the GD32 CMSIS header included via board_config.h). A magic literal drifts silently if the platform header changes and blocks future variant support (see the TODO comment).

🛠️ Proposed fix
 void dwc2_override_hw_params(uint32_t reg_base, struct dwc2_hw_params *hw) {
-    /* HWCFG2 reads 0, this is unknown why */
-    if(reg_base == 0x50000000UL) {
-        hw->num_dev_ep = 4;
-    }
+    /* HWCFG2 reads 0 on GD32C103/E103; supply known values manually. */
+    if (reg_base == USBFS_BASE) {
+        hw->num_dev_ep = CONFIG_USBDEV_EP_NUM - 1; /* highest EP index */
+    }
     /* TODO: For other GD32, potentially will need to update like this as well*/
 }

Note: Verify that USBFS_BASE is defined in your GD32 CMSIS headers (included via board_config.h). If unavailable, define a local constant for this variant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 67 - 73, In dwc2_override_hw_params
update the hardcoded num_dev_ep and magic address: set hw->num_dev_ep to
CONFIG_USBDEV_EP_NUM - 1 (since num_dev_ep is the highest zero-based EP index
used by the core loops) instead of 4, and replace the literal 0x50000000UL with
the platform constant (e.g., USBFS_BASE from your GD32 CMSIS headers included
via board_config.h); if USBFS_BASE may be missing on some variants, add a local
conditional fallback define for that platform constant and use it in the
reg_base comparison.

@sakumisu
Copy link
Copy Markdown
Collaborator

Thanks, you do the better solution for gd patches, gd official removed hwcfg registers so we cannot read, this patches will merge in next version.

@minh7a6
Copy link
Copy Markdown
Author

minh7a6 commented Apr 26, 2026

Thanks! It is odd why they did that. It makes probing much more difficult than necessary. I only override the number of EPs for now. But from the TinyUSB port and my own testing, all hwcfg registers and snpsid are 0. Hopefully some representatives from GD can describe more what the values are for these

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@port/dwc2/usb_glue_gd_arm.c`:
- Around line 42-48: usb_dc_low_level_init currently only enables the IRQ
(NVIC_EnableIRQ(USBFS_IRQn)) but omits USB clock and GPIO setup; either
implement the missing hardware init (enable USBFS peripheral clock via
rcu_periph_clock_enable(RCU_USBFS), configure USB 48MHz prescaler with
rcu_usb_clock_config(...), and configure USB D+ / D- pins (e.g., PA11/PA12 or
PB14/PB15) to the USB AF) within usb_dc_low_level_init (and undo/disable in
usb_dc_low_level_deinit), or add a clear comment at the top of this file stating
that board_config.h is responsible for RCU and GPIO setup so callers know the
contract; reference usb_dc_low_level_init, usb_dc_low_level_deinit,
NVIC_EnableIRQ, USBFS_IRQn, rcu_periph_clock_enable, rcu_usb_clock_config, and
PA11/PA12 (or PB14/PB15) when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 517ae6c7-e5a1-435d-99d1-3cc2f61a190f

📥 Commits

Reviewing files that changed from the base of the PR and between 00048c8 and 43304a2.

📒 Files selected for processing (1)
  • port/dwc2/usb_glue_gd_arm.c

Comment on lines +42 to +48
void usb_dc_low_level_init(uint8_t busid) {
NVIC_EnableIRQ(USBFS_IRQn);
}

void usb_dc_low_level_deinit(uint8_t busid) {
NVIC_DisableIRQ(USBFS_IRQn);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm whether USBFS clock / GPIO setup happens elsewhere in the tree.
rg -nP -C2 'RCU_USBFS|rcu_usb_clock_config|rcu_periph_clock_enable\s*\(\s*RCU_USBFS'
rg -nP -C2 '\b(GPIO|gpio)_(init|mode_set|af_set).*PA1[12]\b'
fd -t f -e c -e h | xargs rg -lP 'usb_dc_low_level_init' 2>/dev/null

Repository: cherry-embedded/CherryUSB

Length of output: 996


🏁 Script executed:

cat -n port/dwc2/usb_glue_gd.c

Repository: cherry-embedded/CherryUSB

Length of output: 1489


🏁 Script executed:

cat -n port/dwc2/usb_glue_gd_arm.c

Repository: cherry-embedded/CherryUSB

Length of output: 2892


🏁 Script executed:

rg -n 'RCU_USBFS|rcu_usb_clock_config' port/dwc2/ -A 3 -B 3

Repository: cherry-embedded/CherryUSB

Length of output: 51


🏁 Script executed:

rg -n 'PA1[12]|PB1[45]' port/dwc2/ -A 2 -B 2

Repository: cherry-embedded/CherryUSB

Length of output: 530


🏁 Script executed:

wc -l port/dwc2/usb_glue_gd.c

Repository: cherry-embedded/CherryUSB

Length of output: 96


🏁 Script executed:

cat -n port/dwc2/usb_glue_gd.c | tail -100

Repository: cherry-embedded/CherryUSB

Length of output: 1489


🏁 Script executed:

find . -name '*gd*' -path '*/port/*' -type f | grep -E '\.(c|h)$'

Repository: cherry-embedded/CherryUSB

Length of output: 125


🏁 Script executed:

rg -n 'RCU_USBFS|rcu_periph_clock_enable.*USBFS|rcu_usb_clock_config' --type c --type h

Repository: cherry-embedded/CherryUSB

Length of output: 51


🏁 Script executed:

rg -n 'board_config|board.h' port/dwc2/usb_glue_gd_arm.c

Repository: cherry-embedded/CherryUSB

Length of output: 98


🏁 Script executed:

find . -name 'board_config.h' -o -name 'board.h' | head -5

Repository: cherry-embedded/CherryUSB

Length of output: 51


🏁 Script executed:

rg -n 'rcu_periph_clock_enable|RCU_' --type c --type h | head -20

Repository: cherry-embedded/CherryUSB

Length of output: 51


🏁 Script executed:

rg -n 'gpio.*init|GPIO.*AFIO' --type c --type h -i | grep -i 'gd32\|pa1[12]\|pb1[45]' | head -10

Repository: cherry-embedded/CherryUSB

Length of output: 51


🏁 Script executed:

rg -n 'usb_dc_low_level_init|usb_dc_init' -A 10 port/ --type c | head -80

Repository: cherry-embedded/CherryUSB

Length of output: 4321


🏁 Script executed:

rg -n 'rcu_periph_clock_enable|clock.*enable' port/ --type c -B2 -A2 | grep -E '(usb|USB)' | head -30

Repository: cherry-embedded/CherryUSB

Length of output: 1805


🏁 Script executed:

ls -la port/dwc2/ | grep glue

Repository: cherry-embedded/CherryUSB

Length of output: 684


🏁 Script executed:

cat -n port/dwc2/usb_glue_st.c | head -100

Repository: cherry-embedded/CherryUSB

Length of output: 3461


🏁 Script executed:

cat -n port/dwc2/usb_glue_esp.c | head -100

Repository: cherry-embedded/CherryUSB

Length of output: 4179


🏁 Script executed:

rg -n 'void usb_dc_low_level_init' port/dwc2/ -A 15 --type c

Repository: cherry-embedded/CherryUSB

Length of output: 7618


usb_dc_low_level_init() only enables NVIC; clock and GPIO setup are missing or undocumented.

The function only calls NVIC_EnableIRQ(USBFS_IRQn). Typical GD32 USBFS initialization also requires:

  • Enabling the USBFS bus clock (rcu_periph_clock_enable(RCU_USBFS))
  • Configuring USB clock prescaler for 48 MHz (rcu_usb_clock_config)
  • Setting up PA11/PA12 (or PB14/PB15) GPIO alternate-function pins

Other DWC2 port implementations (ST, Nation, Infineon, HC, Kendryte) perform such setup in their usb_dc_low_level_init() functions. If your board code handles this via the included board_config.h, add a comment documenting the contract; otherwise this file is incomplete.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@port/dwc2/usb_glue_gd_arm.c` around lines 42 - 48, usb_dc_low_level_init
currently only enables the IRQ (NVIC_EnableIRQ(USBFS_IRQn)) but omits USB clock
and GPIO setup; either implement the missing hardware init (enable USBFS
peripheral clock via rcu_periph_clock_enable(RCU_USBFS), configure USB 48MHz
prescaler with rcu_usb_clock_config(...), and configure USB D+ / D- pins (e.g.,
PA11/PA12 or PB14/PB15) to the USB AF) within usb_dc_low_level_init (and
undo/disable in usb_dc_low_level_deinit), or add a clear comment at the top of
this file stating that board_config.h is responsible for RCU and GPIO setup so
callers know the contract; reference usb_dc_low_level_init,
usb_dc_low_level_deinit, NVIC_EnableIRQ, USBFS_IRQn, rcu_periph_clock_enable,
rcu_usb_clock_config, and PA11/PA12 (or PB14/PB15) when making the change.

Signed-off-by: sakumisu <1203593632@qq.com>
@sakumisu sakumisu force-pushed the master branch 2 times, most recently from 4eb3bc1 to c9625ff Compare April 28, 2026 08:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants