-
Notifications
You must be signed in to change notification settings - Fork 374
Add isochronous transfer support to fsdev by configuring double-buffering for isochronous endpoints. #372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
…ring for isochronous endpoints.
WalkthroughThis pull request adds ISOCHRONOUS endpoint support with double-buffering to the FSDEV USB device controller. Changes include removing ISO assertion checks, implementing dual-buffer PMA setup during endpoint initialization, updating endpoint write operations to handle ISO buffers, and extending the IRQ handler to manage ISO transfers with proper data toggle handling. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant FSDEV as FSDEV USB<br/>(Endpoint)
participant DMA as PMA<br/>(Dual Buffers)
participant ISR as IRQHandler
App->>FSDEV: Open ISO Endpoint
FSDEV->>DMA: Setup DBUF0 & DBUF1<br/>addresses
FSDEV->>FSDEV: Initialize DBUF counts<br/>& DTOG bits
Note over App,ISR: OUT (CTR_RX) ISO Transfer
App->>FSDEV: usbd_ep_start_read()
FSDEV->>DMA: RX_VALID (awaiting data)
DMA->>ISR: Data received in DBUF0
ISR->>DMA: Read from active buffer<br/>(DBUF0 or DBUF1 per DTOG RX)
ISR->>FSDEV: Free user buffer<br/>(toggle DTOG)
ISR->>App: Transfer complete event
Note over App,ISR: IN (CTR_TX) ISO Transfer
App->>FSDEV: usbd_ep_start_write()
FSDEV->>DMA: Write to active DBUF<br/>(per TX DTOG)
FSDEV->>DMA: TX status = VALID
DMA->>ISR: Transmission complete (CTR_TX)
ISR->>FSDEV: Select next DBUF counter<br/>(DBUF0_CNT or DBUF1_CNT)
ISR->>ISR: Advance transfer state
alt Transfer complete
ISR->>App: Completion event
else More data
ISR->>DMA: Write next buffer<br/>& set TX_VALID
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
| PCD_CLEAR_EP_KIND(USB, ep_idx); | ||
| /* Set buffer address for double buffered mode */ | ||
| PCD_SET_EP_DBUF_ADDR(USB, ep_idx, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, | ||
| g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps); | ||
|
|
||
| /* Clear the data toggle bits for the endpoint IN/OUT */ | ||
| PCD_CLEAR_RX_DTOG(USB, ep_idx); | ||
| PCD_CLEAR_TX_DTOG(USB, ep_idx); | ||
|
|
||
| /* Set endpoint RX count */ | ||
| PCD_SET_EP_DBUF_CNT(USB, ep_idx, 0, ep->wMaxPacketSize); | ||
|
|
||
| /* Set endpoint RX to valid state */ | ||
| PCD_SET_EP_RX_STATUS(USB, ep_idx, USB_EP_RX_VALID); | ||
| PCD_SET_EP_TX_STATUS(USB, ep_idx, USB_EP_TX_DIS); | ||
| g_fsdev_udc.pma_offset += USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize * 2); | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double buffer never enabled for ISO endpoints
PCD_CLEAR_EP_KIND disables the double-buffer flag. Without setting EP_KIND/PCD_SET_EP_DBUF, the hardware stays single-buffered, so all the subsequent DBUF address/count programming and DTOG handling never take effect. The new ISO paths will therefore malfunction. Please enable the DBUF bit (both OUT and IN branches) instead of clearing it.
- PCD_CLEAR_EP_KIND(USB, ep_idx);
+ PCD_SET_EP_DBUF(USB, ep_idx);
...
- PCD_CLEAR_EP_KIND(USB, ep_idx);
+ PCD_SET_EP_DBUF(USB, ep_idx);📝 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.
| PCD_CLEAR_EP_KIND(USB, ep_idx); | |
| /* Set buffer address for double buffered mode */ | |
| PCD_SET_EP_DBUF_ADDR(USB, ep_idx, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, | |
| g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps); | |
| /* Clear the data toggle bits for the endpoint IN/OUT */ | |
| PCD_CLEAR_RX_DTOG(USB, ep_idx); | |
| PCD_CLEAR_TX_DTOG(USB, ep_idx); | |
| /* Set endpoint RX count */ | |
| PCD_SET_EP_DBUF_CNT(USB, ep_idx, 0, ep->wMaxPacketSize); | |
| /* Set endpoint RX to valid state */ | |
| PCD_SET_EP_RX_STATUS(USB, ep_idx, USB_EP_RX_VALID); | |
| PCD_SET_EP_TX_STATUS(USB, ep_idx, USB_EP_TX_DIS); | |
| g_fsdev_udc.pma_offset += USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize * 2); | |
| } else { | |
| PCD_SET_EP_DBUF(USB, ep_idx); | |
| /* Set buffer address for double buffered mode */ | |
| PCD_SET_EP_DBUF_ADDR(USB, ep_idx, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, | |
| g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps); | |
| /* Clear the data toggle bits for the endpoint IN/OUT */ | |
| PCD_CLEAR_RX_DTOG(USB, ep_idx); | |
| PCD_CLEAR_TX_DTOG(USB, ep_idx); | |
| /* Set endpoint RX count */ | |
| PCD_SET_EP_DBUF_CNT(USB, ep_idx, 0, ep->wMaxPacketSize); | |
| /* Set endpoint RX to valid state */ | |
| PCD_SET_EP_RX_STATUS(USB, ep_idx, USB_EP_RX_VALID); | |
| PCD_SET_EP_TX_STATUS(USB, ep_idx, USB_EP_TX_DIS); | |
| g_fsdev_udc.pma_offset += USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize * 2); | |
| } else { |
🤖 Prompt for AI Agents
In port/fsdev/usb_dc_fsdev.c around lines 184-200, the code calls
PCD_CLEAR_EP_KIND which disables double-buffering so the subsequent DBUF
address/count programming and DTOG handling are ignored for ISO endpoints;
replace the clear with the DBUF enable call(s) (e.g., PCD_SET_EP_KIND /
PCD_SET_EP_DBUF or the platform-specific macro) for both the OUT and IN branches
so the hardware is configured double-buffered, then leave the DBUF address, DBUF
count and DTOG clears as-is to complete proper double-buffer setup.
| /* manage double buffer bulk out */ | ||
| /* free EP OUT Buffer */ | ||
| PCD_FREE_USER_BUFFER(USB, ep_idx, 0U); | ||
|
|
||
| if ((PCD_GET_ENDPOINT(USB, ep_idx) & USB_EP_DTOG_RX) != 0U) { | ||
| /* read from endpoint BUF0Addr buffer */ | ||
| read_count = (uint16_t)PCD_GET_EP_DBUF0_CNT(USB, ep_idx); | ||
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | ||
| } else { | ||
| /* read from endpoint BUF1Addr buffer */ | ||
| read_count = (uint16_t)PCD_GET_EP_DBUF1_CNT(USB, ep_idx); | ||
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps, (uint16_t)read_count); | ||
| } | ||
| } else { | ||
| read_count = PCD_GET_EP_RX_CNT(USB, ep_idx); | ||
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | ||
| } | ||
| g_fsdev_udc.out_ep[ep_idx].xfer_buf += read_count; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Freeing ISO OUT buffer before copy corrupts data
Calling PCD_FREE_USER_BUFFER before copying toggles the SW_BUF bit, so the controller may reuse that PMA slot for the next ISO frame while we are still reading it. The PMA read must happen first, then free the user buffer. Please move the PCD_FREE_USER_BUFFER call to after the read completes.
- /* free EP OUT Buffer */
- PCD_FREE_USER_BUFFER(USB, ep_idx, 0U);
-
if ((PCD_GET_ENDPOINT(USB, ep_idx) & USB_EP_DTOG_RX) != 0U) {
/* read from endpoint BUF0Addr buffer */
read_count = (uint16_t)PCD_GET_EP_DBUF0_CNT(USB, ep_idx);
fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count);
} else {
/* read from endpoint BUF1Addr buffer */
read_count = (uint16_t)PCD_GET_EP_DBUF1_CNT(USB, ep_idx);
fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps, (uint16_t)read_count);
}
+
+ /* free EP OUT Buffer */
+ PCD_FREE_USER_BUFFER(USB, ep_idx, 0U);📝 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.
| /* manage double buffer bulk out */ | |
| /* free EP OUT Buffer */ | |
| PCD_FREE_USER_BUFFER(USB, ep_idx, 0U); | |
| if ((PCD_GET_ENDPOINT(USB, ep_idx) & USB_EP_DTOG_RX) != 0U) { | |
| /* read from endpoint BUF0Addr buffer */ | |
| read_count = (uint16_t)PCD_GET_EP_DBUF0_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | |
| } else { | |
| /* read from endpoint BUF1Addr buffer */ | |
| read_count = (uint16_t)PCD_GET_EP_DBUF1_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps, (uint16_t)read_count); | |
| } | |
| } else { | |
| read_count = PCD_GET_EP_RX_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | |
| } | |
| g_fsdev_udc.out_ep[ep_idx].xfer_buf += read_count; | |
| /* manage double buffer bulk out */ | |
| if ((PCD_GET_ENDPOINT(USB, ep_idx) & USB_EP_DTOG_RX) != 0U) { | |
| /* read from endpoint BUF0Addr buffer */ | |
| read_count = (uint16_t)PCD_GET_EP_DBUF0_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | |
| } else { | |
| /* read from endpoint BUF1Addr buffer */ | |
| read_count = (uint16_t)PCD_GET_EP_DBUF1_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr + g_fsdev_udc.out_ep[ep_idx].ep_mps, (uint16_t)read_count); | |
| } | |
| /* free EP OUT Buffer */ | |
| PCD_FREE_USER_BUFFER(USB, ep_idx, 0U); | |
| } else { | |
| read_count = PCD_GET_EP_RX_CNT(USB, ep_idx); | |
| fsdev_read_pma(USB, g_fsdev_udc.out_ep[ep_idx].xfer_buf, g_fsdev_udc.out_ep[ep_idx].ep_pma_addr, (uint16_t)read_count); | |
| } | |
| g_fsdev_udc.out_ep[ep_idx].xfer_buf += read_count; |
🤖 Prompt for AI Agents
In port/fsdev/usb_dc_fsdev.c around lines 488 to 505, the PCD_FREE_USER_BUFFER
call is currently executed before reading the PMA which can toggle SW_BUF and
let the controller reuse the PMA while we're still copying; move the
PCD_FREE_USER_BUFFER(USB, ep_idx, 0U) invocation to immediately after the
fsdev_read_pma(...) completes for both the double-buffer branches and the
single-buffer branch so the PMA is read first and then the buffer is freed.
|
Thanks very much, i will test for this. |
669e41e to
cf85c88
Compare
4ff88ff to
a2352bf
Compare
Summary by CodeRabbit
New Features
Refactor