Skip to content

Commit de88294

Browse files
authored
Merge pull request #416 from hathach/fix-nrf-called-within-critical
fix nrf hanged (blocking wait) when called within critical section
2 parents dc5445e + ff99941 commit de88294

2 files changed

Lines changed: 73 additions & 24 deletions

File tree

examples/device/cdc_msc/ses/nrf5x/nrf5x.emProject

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
arm_target_device_name="nRF52840_xxAA"
2020
arm_target_interface_type="SWD"
2121
build_treat_warnings_as_errors="Yes"
22-
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;CFG_TUSB_MCU=OPT_MCU_NRF5X"
23-
c_user_include_directories="../../src;$(rootDir)/hw/mcu/nordic/cmsis/Include;$(rootDir)/hw;$(rootDir)/src;$(nrfxDir)/..;$(nrfxDir);$(nrfxDir)/mdk;$(nrfxDir)/hal;$(nrfxDir)/drivers/include;$(nrfxDir)/drivers/src"
22+
c_additional_options="-Wno-error=undef;-Wno-error=unused-parameter;-Wno-error=cast-align;-Wno-error=cast-function-type"
23+
c_preprocessor_definitions="NRF52840_XXAA;__nRF_FAMILY;ARM_MATH_CM4;FLASH_PLACEMENT=1;CFG_TUSB_MCU=OPT_MCU_NRF5X;CFG_TUSB_DEBUG=1"
24+
c_user_include_directories="../../src;$(rootDir)/lib/CMSIS_4/CMSIS/Include;$(rootDir)/hw;$(rootDir)/src;$(nrfxDir)/..;$(nrfxDir);$(nrfxDir)/mdk;$(nrfxDir)/hal;$(nrfxDir)/drivers/include;$(nrfxDir)/drivers/src"
2425
debug_register_definition_file="nrf52840_Registers.xml"
2526
debug_target_connection="J-Link"
2627
gcc_enable_all_warnings="Yes"
@@ -42,11 +43,12 @@
4243
recurse="Yes" />
4344
<folder Name="hw">
4445
<folder Name="bsp">
45-
<folder Name="pca10056">
46-
<file file_name="../../../../../hw/bsp/pca10056/pca10056.c" />
47-
</folder>
4846
<file file_name="../../../../../hw/bsp/ansi_escape.h" />
4947
<file file_name="../../../../../hw/bsp/board.h" />
48+
<file file_name="../../../../../hw/bsp/board.c" />
49+
<folder Name="feather_nrf52840_express">
50+
<file file_name="../../../../../hw/bsp/feather_nrf52840_express/feather_nrf52840_express.c" />
51+
</folder>
5052
</folder>
5153
<folder Name="mcu">
5254
<folder Name="nordic">
@@ -103,16 +105,10 @@
103105
<file file_name="thumb_crt0.s" />
104106
<file file_name="nRF52840_xxAA_s140v6_MemoryMap.xml" />
105107
</folder>
106-
<folder
107-
Name="segger_rtt"
108-
exclude=""
109-
filter="*.c;*.h"
110-
path="../../../../../lib/segger_rtt"
111-
recurse="No" />
112108
<configuration
113109
Name="pca10056"
114110
build_treat_warnings_as_errors="No"
115-
c_preprocessor_definitions="BOARD_PCA10056"
111+
c_preprocessor_definitions=""
116112
linker_memory_map_file="nRF52840_xxAA_MemoryMap.xml" />
117113
<configuration
118114
Name="pca10056 s140v6"
@@ -121,7 +117,16 @@
121117
c_user_include_directories="$(nrfxDir)/../nrf5x/s140_nrf52_6.1.1_API/include;$(nrfxDir)/../nrf5x/s140_nrf52_6.1.1_API/include/nrf52"
122118
debug_start_from_entry_point_symbol="No"
123119
linker_memory_map_file="nRF52840_xxAA_s140v6_MemoryMap.xml" />
120+
<folder Name="SEGGER_RTT">
121+
<folder Name="RTT">
122+
<file file_name="../../../../../lib/SEGGER_RTT/RTT/SEGGER_RTT.c" />
123+
<file file_name="../../../../../lib/SEGGER_RTT/RTT/SEGGER_RTT.h" />
124+
<file file_name="../../../../../lib/SEGGER_RTT/RTT/SEGGER_RTT_Conf.h" />
125+
<file file_name="../../../../../lib/SEGGER_RTT/RTT/SEGGER_RTT_printf.c" />
126+
</folder>
127+
</folder>
124128
</project>
125129
<configuration Name="pca10056" />
126130
<configuration Name="pca10056 s140v6" />
131+
<configuration Name="Feather nRF52840" />
127132
</solution>

src/portable/nordic/nrf5x/dcd_nrf5x.c

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ enum
5151
USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
5252
};
5353

54+
enum
55+
{
56+
EP_COUNT = 8
57+
};
58+
5459
// Transfer descriptor
5560
typedef struct
5661
{
@@ -69,36 +74,73 @@ typedef struct
6974
static struct
7075
{
7176
// All 8 endpoints including control IN & OUT (offset 1)
72-
xfer_td_t xfer[8][2];
77+
xfer_td_t xfer[EP_COUNT][2];
7378

74-
// Only one DMA can run at a time
75-
volatile bool dma_running;
79+
// Number of pending DMA that is started but not handled yet by dcd_int_handler().
80+
// Since nRF can only carry one DMA can run at a time, this value is normally be either 0 or 1.
81+
// However, in critical section with interrupt disabled, the DMA can be finished and added up
82+
// until handled by dcd_init_handler() when exiting critical section.
83+
volatile uint8_t dma_pending;
7684
}_dcd;
7785

7886
/*------------------------------------------------------------------*/
7987
/* Control / Bulk / Interrupt (CBI) Transfer
8088
*------------------------------------------------------------------*/
8189

90+
// NVIC_GetEnableIRQ is only available in CMSIS v5
91+
#ifndef NVIC_GetEnableIRQ
92+
static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
93+
{
94+
if ((int32_t)(IRQn) >= 0)
95+
{
96+
return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
97+
}
98+
else
99+
{
100+
return(0U);
101+
}
102+
}
103+
#endif
104+
82105
// helper to start DMA
83106
static void edpt_dma_start(volatile uint32_t* reg_startep)
84107
{
85108
// Only one dma can be active
86-
if ( _dcd.dma_running )
109+
if ( _dcd.dma_pending )
87110
{
88111
if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk)
89112
{
90-
// If called within ISR, use usbd task to defer later
113+
// Called within ISR, use usbd task to defer later
91114
usbd_defer_func( (osal_task_func_t) edpt_dma_start, (void*) reg_startep, true );
92115
return;
93116
}
94117
else
95118
{
96-
// Otherwise simply block wait
97-
while ( _dcd.dma_running ) { }
119+
if ( __get_PRIMASK() || !NVIC_GetEnableIRQ(USBD_IRQn) )
120+
{
121+
// Called in critical section with interrupt disabled. We have to manually check
122+
// for the DMA complete by comparing current pending DMA with number of ENDED Events
123+
uint32_t ended = 0;
124+
125+
while ( _dcd.dma_pending < ((uint8_t) ended) )
126+
{
127+
ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
128+
129+
for (uint8_t i=0; i<EP_COUNT; i++)
130+
{
131+
ended += NRF_USBD->EVENTS_ENDEPIN[i] + NRF_USBD->EVENTS_ENDEPOUT[i];
132+
}
133+
}
134+
}else
135+
{
136+
// Called in non-critical thread-mode, should be 99% of the time.
137+
// Should be safe to blocking wait until previous DMA transfer complete
138+
while ( _dcd.dma_pending ) { }
139+
}
98140
}
99141
}
100142

101-
_dcd.dma_running = true;
143+
_dcd.dma_pending++;
102144

103145
(*reg_startep) = 1;
104146
__ISB(); __DSB();
@@ -107,8 +149,8 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
107149
// DMA is complete
108150
static void edpt_dma_end(void)
109151
{
110-
TU_ASSERT(_dcd.dma_running, );
111-
_dcd.dma_running = false;
152+
TU_ASSERT(_dcd.dma_pending, );
153+
_dcd.dma_pending = 0;
112154
}
113155

114156
// helper getting td
@@ -282,9 +324,11 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
282324

283325
if ( control_status )
284326
{
285-
// Status Phase also require Easy DMA has to be free as well !!!!
327+
// Status Phase also requires Easy DMA has to be available as well !!!!
328+
// However TASKS_EP0STATUS doesn't trigger any DMA transfer and got ENDED event subsequently
329+
// Therefore dma_running state will be corrected right away
286330
edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
287-
edpt_dma_end();
331+
if (_dcd.dma_pending) _dcd.dma_pending--; // correct the dma_running++ in dma start
288332

289333
// The nRF doesn't interrupt on status transmit so we queue up a success response.
290334
dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, false);

0 commit comments

Comments
 (0)