cancel
Showing results for 
Search instead for 
Did you mean: 

How to integrate slow-write (SPI) FLASH into STM32 USB device library, to return "OK" CSW when FLASH write finishes?

PHolt.1
Senior III

Currently I have it working, but while the FLASH READ is about 200us and can be done wholly inside STORAGE_Read_FS (which is within an ISR), the FLASH WRITE takes ~17ms and while it works fine done wholly inside STORAGE_Write_FS, it needs the disabling of all USB interrupts when the FLASH is accessed elsewhere.

This works as far as the USB host (a PC) is concerned, and apparently USB FLASH sticks do more or less just this (return the "OK" CSW only when the FLASH write has finished), the long USB ISR time of ~17ms creates problems inside my system. For example a) it screws up USB VCP (CDC) operation during a tight series of USB disk writes and b) it disables all lower priority interrupts for the ~17ms FLASH write.

As I say, it works, but only if host-triggered file writes are sporadic. For example a ~30ms gap after each internally initiated FLASH sector write makes it hang together OK, without the host timing out. FLASH reading is OK because I have put in a lot of time to optimise it with DMA.

So I am looking at ways to do this more neatly, which means not returning the "OK" CSW initially (currently it is returned immediately STORAGE_Write_FS returns (if len=0 i.e. the last 64-byte packet has filled the 512 byte sector buffer and the buffer is ready to be flashed) in SCSI_ProcessWrite) and instead subcontracting the block write to an RTOS task and returning the "OK" CSW from that task when the FLASH write has finished.

I am sure lots of people have gone up this path...

The current ST code is OK only if writing to a RAM disk or some such.

Some background to this can be found here:

https://www.eevblog.com/forum/microcontrollers/cleanest-way-to-block-usb-interrupts/

https://www.eevblog.com/forum/microcontrollers/32f417-usb-fs-(not-hs)-and-dma/

I am happy to pay somebody to do this : - )

7 REPLIES 7

Should be able to defer the sending, ie the USB stack (callback/interrupt) is released immediately so as not to block, and then the CSW is returned later once the write completes.

In normal SCSI interactions the other way to deal with long buffered/pending writes is to throw a SENSE CODE that says it's busy/active. This is how CDR/DVDR burners worked.

The host MSC stack does however need to be sufficiently aware that the device might reasonably do this.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

https://www.t10.org/lists/asc-num.htm

ASC/ASQ

04h/08h R LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
PHolt.1
Senior III

I can avoid the "OK" CSW being returned at the start of the flash write (upon exit from STORAGE_Write_FS, following the commencement of a non blocking flash write). That is just a global flag which gets set when the flash write is started, and then I just skip that "if len=0, send OK CSW" call in SCSI_ProcessWrite.

I traced the code today and there are eight calls to SCSI_ProcessWrite, and only on the 8th does the flash sector get written, in STORAGE_Write_FS, which is exactly what one one would expect with 64 byte packets in USB_FS mode (8x64=512).

What is not trivial is working out how to inject the "OK" CSW back into the USB stack, approx 17ms later, from an RTOS task which is obvious asynchronous to the USB stack. It has to be the right form of CSW, corresponding to the USB context. It might be the same CSW packet which was suppressed earlier and that may be a simplified hack, but I still don't know how to send that packet back, from outside the USB stack.

Problem with ST code, and a lot of embedded coding in general, is this single linear flow concept. Windows NT (and model used thru today) has an asynchronous stack that progresses in stages as each completes, it doesn't sit in spin loops waiting, if something is busy, it leaves and comes back later. When all steps are complete it signals back. The steps can happen all at once on one CPU, or be handled by every CPU in the system/core.

USB MSC stacks can be implemented in the same way, request should come in and get started/queued, and it should leave (inbound USB request). The out bound USB response should be queue in the interrupt for the Read/Write interaction on memory, that point the CSW with either be a successful completion code, or an error or status.

I'm sure the STM32 USB could be done like this, not sure the boiler plate is that advanced though, it's likely the simplest thing they could code, test and ship, and still say it works.

The alternative is to sink ALL the data, queue the write and if the write is still pending in the back-ground queue, throw NOT READY codes the host will accept. If the write ultimately fails throw some deferred write failure code.

Disabling interrupts and becoming unresponsive, and stalling is not the way to do this.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Host stacks typically send a TEST UNIT READY (TUR) request, this is the point where you'd typically say NO, and provide a reason or an error. This is usually the better place to do it, than fail a command when you've just said its ready.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
PHolt.1
Senior III

Currently the host, writing a 512 byte sector, gets seven "instant" USBD_OK replies, and the eighth reply is also USBD_OK but 17ms later.

When reading a sector, same thing but instead of 17ms it is 300us.

It works ok with Windows. Apparently flash sticks, using a dumb state machine, do the same thing.

The method I am proposing will result in the same thing as seen by the host, but it will avoid the 17ms hang in the USB stack ISR (of which STORAGE_Write_FS is part-way in a long chain).

Returning USDB_BUSY during the flash write was tried but didn't work for whatever reason. I have no idea whether it should even work. But returning nothing for 17ms and then USDB_OK clearly works.

I don't think the ST USB stack waits anywhere. AFAIK an interrupt is generated on FIFO FULL (when the FIFO has 64 bytes - this is USB FS mode) and then a software loop copies 64 bytes (as 16 32 bit words) from the FIFO into the appropriate offset in a 512 byte buffer. No handshaking; it just copies 64 bytes (or maybe less on the 8th one). So you get 8 of these interrupts. And they are generated from incoming data.

I'd have three questions:

1) Does this actually work? The SCSI commands are now decades old, and a small subset has been borrowed for USB drives. I don't think there is any way to know whether any particular command is supported by any particular host OS

2) Where would one implement it? ISTM the biggest job by far is working out how to return packets to the host

3) The "elephant in the room" is that all USB sticks seem to be 100% compatible, and reportedly they don't do this; they just return nothing (on the 8th 64 byte block) and return USBD_OK when the flash write has finished, which on the big flash chips is probably in the millisecond range

https://www.eevblog.com/forum/microcontrollers/32f417-usb-fs-(not-hs)-and-dma/msg3965405/#msg3965405