ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ux_dcd_ra8_usb_isr.c
Go to the documentation of this file.
1
21
22#define UX_SOURCE_CODE
23
24#include <stdint.h>
25#include <string.h>
26
27#include "ra8_check.h"
28#include "ra8_elc_regs.h"
29#include "ra8_isr.h"
30#include "ra8_log.h"
31#include "ra8_usb_regs.h"
32#include "tx_api.h"
33#include "ux_api.h"
34#include "ux_dcd_ra8_usb.h"
36#include "ux_device_stack.h"
37#include "ux_system.h"
38#include "ux_utility.h"
39
56volatile uint32_t g_isr_invocations = 0U;
57
75volatile uint16_t g_isr_intsts0_or = 0U;
76
90volatile uint32_t g_isr_dvst_count = 0U;
91
102volatile uint32_t g_isr_ctrt_count = 0U;
103
115volatile uint32_t g_isr_valid_count = 0U;
116
124volatile uint32_t g_isr_brdy_count = 0U;
125
133volatile uint32_t g_isr_bemp_count = 0U;
134
145volatile uint32_t g_isr_nrdy_count = 0U;
146
157volatile uint32_t g_isr_sofr_count = 0U;
158
159volatile uint32_t g_dcd_irq_spurious_mask_count = 0U;
160
161/* -------------------------------------------------------------------------- */
162/* USBFS interrupt-storm guard */
163/* */
164/* The USBFS controller re-asserts its NVIC line for RSME / SOFR / status- */
165/* only conditions independent of INTENB0. While the host hammers a NAK'ing */
166/* pipe (e.g. the MSC bulk-OUT before the storage class thread has armed the */
167/* CBW receive) these event-less entries re-fire ~1e5/s and -- via Cortex-M */
168/* exception tail-chaining -- consume 100% CPU, so RTOS thread mode never runs */
169/* and the USBX class thread is permanently starved (GitHub issue #6). */
170/* */
171/* internal_usbfs_isr counts consecutive event-less entries; once that run */
172/* crosses k_ra8_usb_storm_mask_run a real storm is in progress, and */
173/* priv_usbfs_irq_mask() disables the USB IRQ at the NVIC -- handing the */
174/* CPU to thread mode. Recovery is the per-app 1 ms SysTick handler: it calls */
175/* ux_dcd_ra8_usb_irq_reenable(), which zeroes the run counter and re-enables */
176/* the line. The run counter is thus a per-millisecond rate gauge -- a genuine */
177/* storm (~1e3 event-less entries/ms) trips it well within a tick; normal idle */
178/* SOFR (~1/ms) never does, so the guard is behaviour-neutral for the working */
179/* CDC / HID apps. SysTick is the recovery clock (not a ThreadX TX_TIMER) */
180/* because it is an exception handler -- it keeps running even while the storm */
181/* has thread mode, and the ThreadX timer subsystem, starved. */
182/* -------------------------------------------------------------------------- */
183
188typedef enum : uint32_t {
199
200/* -------------------------------------------------------------------------- */
201/* IRQ glue */
202/* -------------------------------------------------------------------------- */
203
222RA8_INTERNAL static void internal_isr_bump_counts(uint16_t intsts0)
223{
224 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_dvst)) != 0U) {
226 }
227 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_ctrt)) != 0U) {
229 }
230 if ((intsts0 & (uint16_t)k_ra8_intsts0_mask_valid) != 0U) {
232 }
233 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_brdy)) != 0U) {
235 }
236 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_bemp)) != 0U) {
238 }
239 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_nrdy)) != 0U) {
241 }
242 if ((intsts0 & (uint16_t)(1U << k_ra8_int0_bit_sofr)) != 0U) {
244 }
245}
246
286RA8_INTERNAL static void internal_usbfs_isr(void* ctx)
287{
288 (void)ctx;
290
291 /* HUM Ch 36.2.14 "INTSTS0 : Interrupt Status Register 0", p 1986.
292 * event_msk is the real-event set: INTENB0 (BRDY/NRDY/BEMP/CTRT/
293 * DVST/VBSE) plus VALID. RSME / SOFR / status bits are excluded --
294 * they re-assert the NVIC line on their own and are what storms. */
295 const uint16_t intsts0 = ra8_usb_intsts0_snapshot(k_ra8_usb_speed_fs);
296 const uint16_t event_msk = (uint16_t)((1U << k_ra8_int0_bit_brdy) | (1U << k_ra8_int0_bit_nrdy) |
297 (1U << k_ra8_int0_bit_bemp) | (1U << k_ra8_int0_bit_ctrt) |
298 (1U << k_ra8_int0_bit_dvst) | (1U << k_ra8_int0_bit_vbse) |
299 (uint16_t)k_ra8_intsts0_mask_valid);
300
301 g_isr_intsts0_or |= intsts0;
302
303 if ((intsts0 & event_msk) == 0U) {
308 }
309 } else {
311 }
312
315}
316
342RA8_INTERNAL static void internal_ack_spurious(ra8_usb_speed_t speed, uint16_t intsts0)
343{
344 const uint16_t stuck_mask = (uint16_t)((1U << k_ra8_int0_bit_rsme) | (1U << k_ra8_int0_bit_sofr));
345 if ((intsts0 & stuck_mask) != 0U) {
346 (void)ra8_usb_clear_status(speed, (uint16_t)(intsts0 & stuck_mask));
347 }
348 /* The HS controller latches bus-change/attach/detach in INTSTS1 even
349 * with INTENB1 fully masked; a held latch keeps the shared NVIC line
350 * asserted with INTSTS0 clean (observed live: 440k spurious ISR
351 * entries/s with INTSTS1.BCHG stuck). W0C-ack those bits here. */
352 if (speed == k_ra8_usb_speed_hs) {
353 volatile r_usb_regs_t* const reg = ra8_usb_hs();
354 if (reg != nullptr) {
355 const uint16_t int1_mask =
356 (uint16_t)((1U << k_ra8_int1_bit_bchg) | (1U << k_ra8_int1_bit_dtch) |
357 (1U << k_ra8_int1_bit_attch));
358 reg->INTSTS1 = (uint16_t)~int1_mask;
359 }
360 }
362}
363
387RA8_INTERNAL static void internal_usbhs_isr(void* ctx)
388{
389 (void)ctx;
391
392 /* HUM Ch 36.2.14 "INTSTS0 : Interrupt Status Register 0", p 1986.
393 *
394 * Snapshot INTSTS0 first and gate on event bits (8..15 + VALID in
395 * bit 3). DVSQ[6:4] and VBSTS[7] are status-only and must NEVER be
396 * treated as events; a snapshot of 0x0090 (DVSQ=Default + VBSTS=1)
397 * means "no event pending, just status-bits asserted" and the ISR
398 * must return without touching INTSTS0 to avoid an interrupt storm.
399 * The event_msk below matches the bits enabled in INTENB0; RSME /
400 * SOFR are intentionally excluded because the USBR resume-detect
401 * signal can re-assert the NVIC line independent of INTENB0. */
402 const uint16_t intsts0 = ra8_usb_intsts0_snapshot(k_ra8_usb_speed_hs);
403 const uint16_t event_msk = (uint16_t)((1U << k_ra8_int0_bit_brdy) | (1U << k_ra8_int0_bit_nrdy) |
404 (1U << k_ra8_int0_bit_bemp) | (1U << k_ra8_int0_bit_ctrt) |
405 (1U << k_ra8_int0_bit_dvst) | (1U << k_ra8_int0_bit_vbse) |
406 (uint16_t)k_ra8_intsts0_mask_valid);
407
408 g_isr_intsts0_or |= intsts0;
409
410 if ((intsts0 & event_msk) == 0U) {
412 /* Same storm guard as the FS ISR: a sustained run of event-less
413 * entries means a level condition the acks above cannot clear is
414 * holding the NVIC line. Mask it; the per-app 1 ms SysTick handler
415 * re-enables via ux_dcd_ra8_usb_irq_reenable, so real USB events
416 * resume within one tick. Without this the HS line stormed at
417 * ~440k entries/s and starved thread mode completely (the USBX
418 * storage thread never got a single timeslice). */
422 }
423 return;
424 }
426
428
429 /* ::ra8_usb_dispatch performs the W0C ack with the
430 * ``INTSTS0 = ~(snapshot & ack_bits)`` pattern (HUM Ch 37.2.18 Note
431 * 3 p 2082): writes 0 only to event bits that were observed set, 1
432 * to all other bits (no-op under W0C). VALID is intentionally NOT
433 * acked here -- the SETUP drain in ::ux_dcd_ra8_usb_irq /
434 * ::ra8_usb_read_setup_unconditional clears VALID after copying
435 * USBREQ/USBVAL/USBINDX/USBLENG. */
437}
438
462
485
500void priv_event_cb(void* ctx, ra8_usb_speed_t speed, uint16_t status_mask)
501{
502 (void)ctx;
503 ux_dcd_ra8_usb_irq(speed, status_mask);
504}
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
Event Link Controller (ELC) register layout for the Renesas RA8D2.
ra8_elc_event_t
Partial list of ELC events (populate as drivers need them).
@ k_ra8_elc_event_usbfs_int
USBFS controller interrupt.
@ k_ra8_elc_event_usbhs_int_resume
USBHS combined int + resume.
NVIC + ICU IELSR allocator.
void(* ra8_isr_handler_t)(void *ctx)
Driver-supplied callback invoked on interrupt entry.
Definition ra8_isr.h:128
Lightweight Logging Interface for ra8-firmware.
uint16_t ra8_usb_intsts0_snapshot(ra8_usb_speed_t speed)
Read INTSTS0 without acking any bits.
void ra8_usb_dispatch(ra8_usb_speed_t speed)
Snapshot INTSTS0 and fire the installed event handler.
ra8_usb_speed_t
Selects which controller instance the call targets.
@ k_ra8_usb_speed_hs
High-Speed controller (USBHS @ 0x40351000).
@ k_ra8_usb_speed_fs
Full-Speed controller (USBFS @ 0x40250000).
ra8_err_t ra8_usb_clear_status(ra8_usb_speed_t speed, uint16_t mask)
Clear bits in INTSTS0.
USB Full-Speed + High-Speed controller layout for the Renesas RA8D2.
@ k_ra8_int1_bit_dtch
Device detach detected.
@ k_ra8_int1_bit_attch
Device attach detected.
@ k_ra8_int1_bit_bchg
Bus change.
static volatile r_usb_regs_t * ra8_usb_hs(void)
Get pointer to the USB HS controller register block.
@ k_ra8_intsts0_mask_valid
SETUP packet detect flag.
@ k_ra8_int0_bit_bemp
Buffer-empty interrupt.
@ k_ra8_int0_bit_dvst
Device-state transition.
@ k_ra8_int0_bit_brdy
Buffer-ready interrupt.
@ k_ra8_int0_bit_ctrt
Control-transfer-stage transition.
@ k_ra8_int0_bit_sofr
Start-of-Frame.
@ k_ra8_int0_bit_vbse
VBUS change.
@ k_ra8_int0_bit_nrdy
Buffer-not-ready interrupt.
@ k_ra8_int0_bit_rsme
Resume.
USB FS / HS register window (device-mode subset).
volatile uint16_t INTSTS1
+0x042 Interrupt Status 1.
USBX device-controller-driver (DCD) bridge to ra8_usb.
void ux_dcd_ra8_usb_irq(ra8_usb_speed_t speed, uint16_t intsts0)
ISR-context bottom-half.
USBX device-controller-driver bridge to ra8_usb – per-module cross-translation-unit contract.
void priv_usbfs_irq_mask(void)
Mask the USB IRQ at the NVIC to break an interrupt storm.
volatile uint32_t g_isr_spurious_run
Consecutive event-less ISR entries since the last SysTick re-enable.
volatile uint32_t g_dcd_irq_spurious_mask_count
volatile uint32_t g_isr_sofr_count
Per-bit ISR counter for INTSTS0.SOFR (bit 13) entries.
volatile uint32_t g_isr_brdy_count
Per-bit ISR counter for INTSTS0.BRDY (bit 8) entries.
volatile uint16_t g_isr_intsts0_or
Cumulative bitwise-OR of every INTSTS0 value observed at the entry of internal_usbhs_isr.
ra8_elc_event_t priv_pick_event(ra8_usb_speed_t speed)
Pick the ELC event number for a controller.
ra8_isr_handler_t priv_pick_isr(ra8_usb_speed_t speed)
Pick the ISR trampoline for a controller.
volatile uint32_t g_isr_bemp_count
Per-bit ISR counter for INTSTS0.BEMP (bit 10) entries.
ra8_usb_storm_cfg_t
Tuning constant for the USBFS interrupt-storm guard.
@ k_ra8_usb_storm_mask_run
Consecutive event-less FS ISR entries that trip the NVIC mask.
void priv_event_cb(void *ctx, ra8_usb_speed_t speed, uint16_t status_mask)
ra8_usb_attach_handler trampoline.
volatile uint32_t g_isr_dvst_count
Per-bit ISR counter for INTSTS0.DVST (bit 12) entries.
volatile uint32_t g_isr_invocations
Counter of NVIC ISR entries through internal_usbfs_isr or internal_usbhs_isr.
volatile uint32_t g_isr_nrdy_count
Per-bit ISR counter for INTSTS0.NRDY (bit 9) entries.
static void internal_isr_bump_counts(uint16_t intsts0)
Bump the per-bit ISR counters for an INTSTS0 snapshot.
static void internal_usbfs_isr(void *ctx)
NVIC ISR entry point for the USBFS controller.
static void internal_ack_spurious(ra8_usb_speed_t speed, uint16_t intsts0)
W0C-ack RSME/SOFR on a spurious USB ISR re-entry.
volatile uint32_t g_isr_ctrt_count
Per-bit ISR counter for INTSTS0.CTRT (bit 11) entries.
volatile uint32_t g_isr_valid_count
Per-bit ISR counter for INTSTS0.VALID (bit 3) entries.
static void internal_usbhs_isr(void *ctx)
NVIC ISR entry point for the USBHS controller.