Erratic USB device behavior with RP2040

Hey folks,

I'm trying to port a firmware and device written with C for STM32L4 to Rust for RP2040. I've actually implemented everything and is working fine with my own integration tests which I've written in Python.

But there is a very special problem which I cannot produce with my own integration test. The actual library, which uses this device, is written in C and accesses a virtual com port (a /dev/ttyACM). The library transfers data over the serial port in two bursts. Calls write function two times and sends first 4 bytes as the protocol header and writes the remaining bytes in another system call.

The first 4 bytes always creates a trigger for the USB device, but the second burst does not. Sometimes the device does not receive an interrupt for the second burst and waits for upcoming bytes as the protocol header says "more data is needed". Other side is waiting for response and there is no timeout logic implemented. So both parties wait forever until I manually kill the host process by hand. At that exact moment the device gets an interrupt, processes frame and responds back. Of course no one is there to read that response.

I'm not exactly sure this is due to host buffering and very quick double system write call as the exact scenario is working with STM32L4.

So I need to make sure this is not got stuck on the RP2040 side. What do you suggest to prove that? I'm thinking to read USB SFR's (Special Function Registers) of RP2040 and check whether there is really something going on in the peripheral (Error, Awaiting data etc).

How can I access (read) those SFR's? I'm using Linux, VSCode, cortex-debug and openOCD (custom build for RP2040 support).

Note: Any help to debug the problem on the host side is also appreciated. I've straced the problem and can see write calls. But that's it. Can not see the real data flow as I don't have a proper USB protocol analyzer.

Thanks in advance.

I've solved the problem and would like to write down here if anyone faces a similar thing in the future.

The problem is the duration between the write calls. For a serial port set to 115200, the minimum required transmission time of the 4 byte protocol header is 347us. But the duration was actually 21us. So this was clearly violating some rules. But the reference design was working, so it should be possible somehow.

The USB interrupt was not supplied in such case but reading the serial port until no data exists was the required workaround. Because it was overlapping with the previous USB frame from interrupt stand point, but the peripheral was able to receive transferred information.

Hope this helps.

3 Likes