cancel
Showing results for 
Search instead for 
Did you mean: 

Why does the STM32F407 send a STALL on USB_FS?

waldi
Associate II

Hi,

I'm using STM32F407 and try to establish a USB communication. Until now the device is present in the system and is also able to send and receive bulk transfers. To test the functionality I wrote a simple example:

if MCU receives a string of 5 characters it responds with 60 characters.

The response:

dieptsiz1.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
dieptsiz1.b.xfrsiz = 60;
dieptsiz1.b.pktcnt = 1;
USB.INEP_REGS[1]->DIEPTSIZ = dieptsiz1.d32;
USB.INEP_REGS[1]->DIEPCTL |= DIEPCTLx_EPENA|DIEPCTLx_CNAK;
for (uint32_t i = 0; i < 15; i++){
	*(USB).DFIFO[1]=USB_buffer[i];
}

If transfer is complete I set

USB.INEP_REGS[1]->DIEPCTL |= DIEPCTLx_SNAK;

in IN(1) Transfer completed interrupt to prevent firing ITTXFE (IN token received when TxFIFO is empty) which is activated.

This is working 5 to 10 times but suddenly the MCU does not respond normally. Instead of sending the next 60 characters it responses with status 0xc0000004 - STALL_PID.

0690X000009ZcDuQAK.png

Why does the MCU send a STALL?

19 REPLIES 19

What's the read-out content of the relevant DIEPCTL, DIEPTSIZ and DTXFSTS when the problem occurs? Is their content as expected?

How are the Tx FIFOs set up?

JW

waldi
Associate II
sent[siz1cnt].diepint1_1.d32 = USB.INEP_REGS[1]->DIEPINT;
sent[siz1cnt].diepctl1_1.d32 = USB.INEP_REGS[1]->DIEPCTL;
sent[siz1cnt].dieptsiz1_1.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
sent[siz1cnt].dtxfsts1_1 = USB.INEP_REGS[1]->DTXFSTS;
 
dieptsiz1.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
dieptsiz1.b.xfrsiz = 60;
dieptsiz1.b.pktcnt = 1;
USB.INEP_REGS[1]->DIEPTSIZ = dieptsiz1.d32;
USB.INEP_REGS[1]->DIEPCTL |= DIEPCTLx_EPENA|DIEPCTLx_CNAK;
for (uint32_t i = 0; i < 15; i++){
	*(USB).DFIFO[1]=siz1cnt;//USB_buffer[i];
}
sent[siz1cnt].diepint1_2.d32 = USB.INEP_REGS[1]->DIEPINT;
sent[siz1cnt].diepctl1_2.d32 = USB.INEP_REGS[1]->DIEPCTL;
sent[siz1cnt].dieptsiz1_2.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
sent[siz1cnt].dtxfsts1_2 = USB.INEP_REGS[1]->DTXFSTS;

DIEPINT before and after:0690X000009Zcj7QAC.png

DIEPCTL before and after:

0690X000009ZcixQAC.png

DIEPTSIZ & DTXFSTS before and after:

0690X000009ZcjMQAS.png

FIFO set up:

USB.GREGS->GRXFSIZ &=~ GRXFSIZ_RXFD;
USB.GREGS->GRXFSIZ |= RxFIFO_SIZE << GRXFSIZ_RXFD_POS;
//#define RxFIFO_SIZE				0x23
 
USB.GREGS->DIEPTXF0_HNPTXFSIZ &=~ ((DIEPTXF0_NPTXFD)|(DIEPTXF0_NPTXFSA));
USB.GREGS->DIEPTXF0_HNPTXFSIZ |= (Tx0_FIFO_SIZE << DIEPTXF0_NPTXFD_POS)|(RxFIFO_SIZE << DIEPTXF0_NPTXFSA_POS);
//#define Tx0_FIFO_SIZE			0x10
 
USB.GREGS->DIEPTXF[0] &=~ ((DIEPTXFx_INEPTXFD_MSK)|(DIEPTXFx_INEPTXSA_MSK));
USB.GREGS->DIEPTXF[0] |= ((0x140-(RxFIFO_SIZE + Tx0_FIFO_SIZE)) << DIEPTXF0_NPTXFD_POS)|((RxFIFO_SIZE + Tx0_FIFO_SIZE) << DIEPTXF0_NPTXFSA_POS);
//rest, 0x140 - (0x10 + 0x23)
 
USB.GREGS->DIEPTXF[1] &=~ (DIEPTXFx_INEPTXFD_MSK)|(DIEPTXFx_INEPTXSA_MSK);
USB.GREGS->DIEPTXF[1] = (0x0 << DIEPTXF0_NPTXFD_POS)|(0x140 << DIEPTXF0_NPTXFSA_POS);
//nothing
 
USB.GREGS->DIEPTXF[2] &=~ (DIEPTXFx_INEPTXFD_MSK)|(DIEPTXFx_INEPTXSA_MSK);
USB.GREGS->DIEPTXF[2] = (0x0 << DIEPTXF0_NPTXFD_POS)|(0x140 << DIEPTXF0_NPTXFSA_POS);
//nothing

waldi
Associate II

Register content before and after failed response.

I can't see anything irregular, sorry.

I know this is a hard question to answer, but does the STALL response start only after the last transfer has been started? I mean, couldn't STALL result from something else, that happened already before the routine starting the last transfer (and then that the last transfer has not completed is only a consequence of this)?

A bus protocol decoder would help. There are relatively cheap options around, at least for FS, e.g. https://www.totalphase.com/products/beagle-usb12/, and also some LA/oscilloscopes can decode USB, e.g. I use http://www.asix.net/dbg_sigma.htm with the USB licence.

JW

waldi
Associate II
  1. Every transfer is initiated manually by sending 5 characters to MCU. Before the last transfer I don't see any STALL or something irregular.
  2. I'm not 100% sure it is a real STALL, because this information I got only from Device Monitoring Studio. The flag in DIEPCTL doesn't show STALL. I thought it could also be some automatic feature like:

"Bit 2 NZLSOHSK: Non-zero-length status OUT handshake

The application can use this field to select the handshake the core sends on receiving a

nonzero-length data packet during the OUT transaction of a control transfer’s Status stage.

1: Send a STALL handshake on a nonzero-length status OUT transaction and do not send

the received OUT packet to the application.

0: Send the received OUT packet to the application (zero-length or nonzero-length) and send a handshake based on the NAK and STALL bits for the endpoint in the Device endpoint control register."

Thank you for USB beagle hint. I thought I could avoid it)

I have separated the transfer init, which is loaded if I receive 5 chars:

dieptsiz1.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
dieptsiz1.b.xfrsiz = 60;
dieptsiz1.b.pktcnt = 1;
USB.INEP_REGS[1]->DIEPTSIZ = dieptsiz1.d32;
USB.INEP_REGS[1]->DIEPCTL |= DIEPCTLx_EPENA|DIEPCTLx_CNAK;

and transfer load, which is stats if I receive 2 chars:

if ((received_byte_count ==2)){
	for (uint32_t i = 0; i < 15; i++){
		*(USB).DFIFO[1]=siz1cnt;
	}
	received_byte_count=0;
}

The behavior is the same after ca. 10 successful transmissions. And: the STALL is detected after transfer load:

0690X000009ZcwuQAC.png

waldi
Associate II

Another very important point: If I load the transmission as fast as I can, looking for transfer complete interrupt and do immediate the next load, then the data transmission has NO errors, no STALLs are emitted or ITTXFE(IN token received when TxFIFO is empty).

		//received_byte_count=0; // if commented out continuous load after receiving first 5 chars
		USB_transfer_complete=0; //is set in DIEPINT_XFRC (IN Transfer completed interrupt)
		dieptsiz1.d32 = USB.INEP_REGS[1]->DIEPTSIZ;
		dieptsiz1.b.xfrsiz = 60;
		dieptsiz1.b.pktcnt = 1;
		USB.INEP_REGS[1]->DIEPTSIZ = dieptsiz1.d32;
		USB.INEP_REGS[1]->DIEPCTL |= DIEPCTLx_EPENA|DIEPCTLx_CNAK;
 
		for (uint32_t i = 0; i < 15; i++){
			*(USB).DFIFO[1]=siz1cnt;//USB_buffer[i];
		}
		do{} while(USB_transfer_complete==0);//wait until DIEPINT_XFRC, then start next transmission

The Synopsys OTG IP is very fragile and easily breaks down in surprising ways when it's not handled exactly as the IP authors intended, even if you'd think your actions are reasonable. Therefore, one way to handle such problems is to swallow the frog and analyze/follow closely the Cube or whatever other implementations there are out there for this IP.

JW

PS What happens if you decrease the Tx FIFO space for this EP?

waldi
Associate II

I've reduced the EP1 TxFIFO size to 16 words, after 30 transmission the same result.

I should mention, the STALL response I see in Device Monitor Studio, whereas MCU shows this error as ITTXFE for EP1.

waldi
Associate II

My problem is that the ITTXFE(IN token received when TxFIFO is empty) interrupt is deactivated and not analyzed in the VCP example (en.stm32_f105-07_f2_f4_usb-host-device_lib). I thought it's there for a reason and I have activated it, trying to avoid its firing by setting SNAK after successful transmission. In the VCP example ITTXFE is active all the time. This USB implementation is really ugly and weird.

Is the USB hardware used in STM32F7 the same? Synopsys?