Thanks to visit codestin.com
Credit goes to github.com

Skip to content

stm32/usb_cdc: Double data speed sending from micropython to computer #6172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

andrewleech
Copy link
Contributor

stm32/usb_cdc: Fix tx ringbuffer to remove full == size-1 limitation.

The current usb virtual serial port tx ringbuffer is the basic implementation where it can only be filled to a maximum of buffer size-1.
For a 1024 size buffer this means the largest packet that can be sent is 1023.

Once this is sent though, the next byte copied in goes to the final byte in the buffer, so must be sent as a 1 byte packet before the read pointer can be wrapped around. So in large streaming transfers, watching the usb sniffer you basically get alternating 1023 byte packets then 1 byte packets.

By switching the ringbuffer to the different scheme that doesn't have the full limitation, we can get constant 1023 packets.

For a simple explanation of the two schemes, look at the first and last implementations discussed here: https://www.snellman.net/blog/archive/2016-12-13-ring-buffers/

Note: I have not merged this with the circular buffer from ringbuf.h due to the need to read from the buffer in chunks rather than single bytes, as well as the de-coupled copy of the read pointer.

The RX buffer could likely do with a similar change, though as it's not read from in chunks like the TX buffer it doesn't present the same issue, all that's lost is one byte capacity of the buffer.

@rhempel
Copy link

rhempel commented Jun 24, 2020

I have used this ring buffer method ever since I saw the article :-) we were just sniffing the USB traffic the other day and noticed this ...

1 similar comment
@rhempel
Copy link

rhempel commented Jun 24, 2020

I have used this ring buffer method ever since I saw the article :-) we were just sniffing the USB traffic the other day and noticed this ...

}

static uint16_t inline usbd_cdc_tx_buffer_size(usbd_cdc_itf_t *cdc) {
return tx_waiting = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what the intention here is, maybe just delete tx_waiting = ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy/paste mistake - I had fixed that but the fix must have been on the test branch that didn't get pushed up here again


static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) {
cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data;
cdc->tx_buf_ptr_in = cdc->tx_buf_ptr_in + 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ++ or += 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for sure, yes this long hand version of the line was left over from a gradual test/transition to this algorithm which missed the final clean up.

@dpgeorge
Copy link
Member

Thanks, this is a nice find!

My VCP speed tests show about a 5-10% increase in throughput (up to about 9.8MBits/s on an FS port). Did you measure a double increase for a specific scenario of data transfer?

This was proposed for ringbuf in #4920, and as noted there it introduces a restriction on the size of the buffer: it must be a power of 2, and the maximum size is half of the size of the index (in this case the index is 16-bit, so the maximum size would be 32767 bytes rounded to 16384 for a power-of-2, if I understand correctly).

I think those restrictions are acceptable for USB buffer sizes, and the existing configuration values already satisfy them. But there's definitely going to need to be a big notice (in usbd_cdc_interface.h I guess) stating these restrictions.

@andrewleech
Copy link
Contributor Author

andrewleech commented Jun 26, 2020

The speed up does appear to depend on the driver being used on the computer, with all my testing being on windows.
With usbserial.sys driver on windows it's not as noticeable, however with WinUSB I do get a more dramatic difference.

Logs from firmware side using USB_VCP().send(data):

before: sent 2457600 bytes in 6095ms - 3.08Mbit
after: sent 2457600 bytes in 4723ms - 3.97Mbit
though the other day with the patch I was getting as fast as - nearly twice as fast as without the change
sent 2457600 bytes in 3433ms - 5.46 MBit/s

I presume your testing was on linux? I'm not surprised the usb driver works better there...

Before this change, the ringbuffer was already restricted to powers of two sizing because it was using a mask based method of rollover, the comment on the define where the size is configured was wrong / out of date. I've updated it now.

@dpgeorge
Copy link
Member

I presume your testing was on linux?

Yes. Sending various packet sizes in both directions, using pyserial on the PC side.

Before this change, the ringbuffer was already restricted to powers of two sizing because it was using a mask based method of rollover

True!

}

static uint16_t usbd_cdc_tx_send_length(usbd_cdc_itf_t *cdc) {
uint16_t to_end = USBD_CDC_TX_DATA_SIZE - usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_out) ;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no space before ;


static void usbd_cdc_tx_buffer_put(usbd_cdc_itf_t *cdc, uint8_t data) {
cdc->tx_buf[usbd_cdc_tx_buffer_mask(cdc->tx_buf_ptr_in)] = data;
cdc->tx_buf_ptr_in ++;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no space before ++

cdc->tx_buf_ptr_in ++;
}

static uint8_t* usbd_cdc_tx_buffer_getp(usbd_cdc_itf_t *cdc, uint16_t len) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* needs to go after the space (see uncrustify output in Travis)

@andrewleech
Copy link
Contributor Author

Sorry yes, I need to get into a strict habit of running codeformat.py before push, particularly as you've now made it so easy to run!
Done now.

@dpgeorge
Copy link
Member

Thanks for updating, rebased and merged in e4fcd21

@dpgeorge dpgeorge closed this Jun 29, 2020
tannewt added a commit to tannewt/circuitpython that referenced this pull request Mar 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants