2024-11-01 07:16 PM
Trying to work with the STM32F411 USB OTG peripheral and compiling with Segger Embedded Studio, I'm having trouble clearing out the USB interrupts.
The USB peripheral has IRQ bits that are write-1 to clear. As can be seen in the image below, the breakpoint has triggered under the case that the desired interrupts are cleared out (OTG_HOST_ONLY_IRQs evaluates to 0xF020000A), however, the debugger's register view is showing that they are in fact not cleared. It is suspicious that the value loaded into reg_value happens to be 0x04000020 as this is the stated default value of GINTSTS according to the reference manual (RM0383 rev 3). The CMOD bit is the only bit in this register that is not an interrupt and is correctly showing 1 (host mode) in the debugger's register report, but this bit did not get set when the value of GINTSTS was copied into reg_value. I don't see anything in the reference manual that would indicate GINTSTS needs to be unlocked somehow before doing a read, and the debugger's register report appears to reflect the true value, since if I skip trying to verify the IRQs are cleared and immediately enable the NVIC global interrupt, code branches immediately to the ISR. It is not clear to me why the debug tool correctly reads this register while the compiled code cannot.
2024-11-01 08:40 PM
Thank you for the feedback, although it does not address my issues.
My OP points out that there is a variance in how the GINTSTS register is read between trying to store that value in a local variable vs. what the debugger shows.
I specifically mention that I'm aware that these IRQs are write-1 to clear. As can be seen for just one example, the value I attempt to write to GINTSTS has bit position 1 set which corresponds to the MMIS IRQ, but the debug register view in the lower right is still showing this as set.
2024-11-02 12:55 AM - edited 2024-11-02 12:58 AM
The post you've answered above was an attempt to spam with an AI-generated text.
Are you sure the function appearing to delay for 25ms does indeed that?
Show definition of OTG_FS_GINTSTS.
In cases like these, I look at the disasm.
MMIS bit being set is also a concern. Maybe you already have an interrupt enabled, which may have caused some unexpected operations?
JW
2024-11-04 08:24 AM
Looks like the scam post is gone, I was debating reporting it before.
OTG_FS_GINTSTS is set up as follows :
#define OTG_BASE 0x50000000 // [1]p.38
//
// [1]p.754
#define OTG_FS_GINTSTS (*(volatile uint32_t *)(OTG_BASE+0x014))
The reference page numbers are from RM0383. I haven't been seeing any other issues writing to the USB registers with this setup. Normally I might have used the register definitions provided by Segger but their default header for the STM32F411 doesn't appear to include the USB peripheral for some reason, even though it's there in the XML file used by the debugger.
The timer code was been verified running correctly on an STM32F411 in a different project but I can try to verify it again here. The code that executes before this sets up the main clock to run at 96MHz with 48MHz USB clock, then I reset the USB peripheral via the reset bit via RCC_AHB2RSTR. I also explicitly disable the USB IRQ. I'm not disabling the global IRQ as this is off by default and currently no code executes to turn this on before the USB init code executes.
The code is being compiled as follows:
while (USB_TRUE) {
OTG_FS_GINTSTS |= OTG_HOST_ONLY_IRQs;
2014 movs r0, #20
F2C50000 movt r0, #0x5000
6802 ldr r2, [r0]
210A movs r1, #10
F2CF0120 movt r1, #0xF020
430A orrs r2, r1
6002 str r2, [r0]
--- pfcta_usb_core.c -- 169 --------------------------------
reg_value = OTG_FS_GINTSTS;
6800 ldr r0, [r0]
9002 str r0, [sp, #8]
--- pfcta_usb_core.c -- 170 --------------------------------
if ((reg_value & OTG_HOST_ONLY_IRQs) == 0x00) {
9802 ldr r0, [sp, #8]
4208 tst r0, r1
D100 bne 0x080012B2
--- pfcta_usb_core.c -- 171 --------------------------------
break;
E000 b 0x080012B4
--- pfcta_usb_core.c -- 167 --------------------------------
while (USB_TRUE) {
E7EF b 0x08001294
2024-11-04 10:15 AM
So, we are basically talking about 3 bits here, which were actually cleared just before the test (and read as zero as witnessed by the reg_value variable, but then got set again until they get read by the debugger and displayed (btw., don't you have some sort of "live values" enabled in your IDE, reading out the registers repeatedly as long as they are displayed and/or the program is suspended in breakpoint?)
And we are talking about 3 bits here:
- CIDSCHG - is PA10 set as OTG_FS_ID, and is anything toggling there?
- SOF - this gets set automagically each 1ms if you have the all other things set up as expected for host
- MMIS - this is what is IMO the only intriguing thing here, as it would indicate that there is some access to the device-only registers. I don't know what to think about this one; but also don't see much value in triggering an interrupt upon this flag, except for debugging purposes.
JW
PS. Don't RMW to clear those write-1-to-clear bits; rather, perform a simple write. With the "full mask" you are using this doesn't really matter, but it's a bad style which will bite elsewhere.
2024-11-06 09:13 AM
Hi @Eqqman
CMOD bit reflects whether the OTG_FS controller is operating in host or device mode.
It is not recommended to refer to live register view in the debugger.
There could be timing or synchronization issues when the debugger reads the register values, especially if the interrupts are being set and cleared rapidly. Would you mind sharing your USB traffic?
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2024-11-07 04:35 PM
I changed the code to the following :
// Cfg USB OTG peripheral
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_OTGFSEN);
SET_BIT(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
CLEAR_BIT(RCC->AHB2RSTR, RCC_AHB2RSTR_OTGFSRST);
// Note that the STM32F411 supports OTG, but we will disable this and
// force device to operate as Host- or Device-only
// From [1]p.763
// 1. Program the following fields in the OTG_FS_GAHBCFG register:
// – Global interrupt mask bit GINTMSK = 1
// – RxFIFO non-empty (RXFLVL bit in OTG_FS_GINTSTS)
// – Periodic TxFIFO empty level
// 2. Program the following fields in the OTG_FS_GUSBCFG register:
// – HNP capable bit
// – SRP capable bit
// – FS timeout calibration field
// – USB turnaround time field
// 3. The software must unmask the following bits in the OTG_FS_GINTMSK
// register:
// - OTG interrupt mask
// - Mode mismatch interrupt mask
// 4. The software can read the CMOD bit in OTG_FS_GINTSTS to determine
// whether the OTG_FS controller is operating in host or device mode.
if (USB_role == USB_HOST) {
core_cfg.role = USB_role;
// [1]p.720 Support FS/LS only with 48MHz PHY clock
OTG_FS_HCFG = 0b00000101;
// (4) Host-only mode; setting this bit requires 25ms delay [1]p.703
ATOMIC_SET_BIT(OTG_FS_GUSBCFG, OTG_FHMOD);
USB_Delay_ms(25);
// [1]p.707 The application must clear the OTG_FS_GINTSTS register at
// initialization before unmasking the interrupt bit to avoid any
// interrupts generated prior to initialization. The clearable bits
// are write-1, the remainder must be kept at reset value 0x0400 0020
//OTG_FS_GINTSTS |= OTG_HOST_ONLY_IRQs;
// Note that many ARM chips will take longer than 1 instruction cycle
// to clear an interrupt, leading to some false positives
while (USB_TRUE) {
ATOMIC_SET_BIT(OTG_FS_GINTSTS, OTG_HOST_CLEARABLE_IRQs);
reg_value = OTG_FS_GINTSTS;
if ((reg_value & OTG_HOST_CLEARABLE_IRQs) == 0x00) {
break;
} // end if
} // end while
// (1)
SET_BIT(OTG_FS_GAHBCFG, OTG_GINTMSK);
SET_BIT(OTG_FS_GAHBCFG, OTG_PTXFELVL);
SET_BIT(OTG_FS_GAHBCFG, OTG_TXFELVL);
// (2)
CLEAR_BIT(OTG_FS_GUSBCFG, OTG_HNPCAP);
CLEAR_BIT(OTG_FS_GUSBCFG, OTG_SRPCAP);
// [1]p.704 Table 132 (device only)
//MODIFY_REG(OTG_FS_GUSBCFG, OTG_TRDT_MASK, (0x08 << OTG_TRDT_POS));
// [1]p.704
MODIFY_REG(OTG_FS_GUSBCFG, OTG_TOCAL_MASK, (0x10 << OTG_TOCAL_POS));
// (3)
SET_BIT(OTG_FS_GINTMSK, OTG_OTGINT);
SET_BIT(OTG_FS_GINTMSK, OTG_MMISM);
// Note : The ST description above doesn't tell you to unmask the
// host port IRQ, so the device attach IRQ won't trigger the USB IRQ
SET_BIT(OTG_FS_GINTMSK, OTG_PRTIM);
// ...end ST host port enable description
SET_BIT(OTG_FS_HPRT, OTG_PPWR);
} // end if
At the end of executing this code, reg_value is 0x04000020, so it's still not clear to me why CMOD is not reflected here as the debugger view shows it being high. GINTSTS has the value 0x14000029 which corresponds to the following bits being HIGH : CMOD, SOF, NPTXFE, PTXFE, CIDSCHG. Since none of these are masked, it solves the problem of immediately branching to the USB ISR, but now when I plug something in to the USB connection the PCDET IRQ in FS_HPRT doesn't go off even though it was before. So I'm going to have to do additional research on how the USB peripheral is meant to be configured for hosting.
@waclawek.janI verified that all IRQs I'm attempting to clear when I write to GINSTS are host-accessible. The description for OTG_FS_HCFG says "This register configures the core after power-on. Do not make changes to this register after initializing the host." But it's not clear to me what "initializing the host" counts as. I moved this line up to come before setting the bit to force host-only mode and one or both of these changes seems to have finally stopped MMIS from going off, but I still can't detect device attachment as described.
@FBLGiven that CMOD is not an IRQ bit it's not clear to me why this always appears to read zero when I read GINTSTS. Right now the hardware setup consists of a Nucleo-F411RE board modified to have an 8MHz crystal on the HSE input and a breakout USB cable wired up to PA12/11. We don't have a sniffer so I can't record any actual USB traffic. Given that PCDET isn't going off when I plug something up and I can't start the enumeration process there is no traffic anyway.
2024-11-08 10:52 AM - edited 2024-11-08 11:03 AM
> I moved this line [setting OTG_FS_HCFG ] up to come before setting the bit to force host-only mode and one or both of these changes seems to have finally stopped MMIS from going off, but I still can't detect device attachment as described.
Rings some bells, but it's been a couple of years. May try to look up some old notes but won't promise anything. [EDIT] This. It won't answer your questions, but maybe highlights the associated complexity. [/EDIT]
Ad CMOD - I'd suspect there is some gotcha with this, e.g. you need to read the register twice, or read some other register first (which is what the debugger may do for you) to be propagated to the "user space". Maybe this is related to the OTG machine (which is supposed to switch modes for you, but you've forced the mode switch). Or maybe a longer delay is needed, for any of the zillions of possible reasons. I understand it's annoying not to have the full explanation, but you already know which mode have you selected so it's not fatal for you.
The problem with the OTG module generally is, that it's a very complicated IP with many historically given layers. It's also not ST's in-house IP, and Synopsys (whose documentation is essentially what we have in the OTG chapters in RM, slightly improved by ST during the years), having delivered and got paid, probably simply ignores any further ST's request for clarification/help.
JW