tinygo icon indicating copy to clipboard operation
tinygo copied to clipboard

machine: modify UART and USB CDC ACM Read() to be blocking

Open xudongzheng opened this issue 1 year ago • 4 comments

Using a callback for receiving serial data eliminates the need for polling.

So far I've only modified the files for RP2040 as that's what I'm testing on. If this PR is in the right direction, I will modify type UART struct for the other boards as well.

xudongzheng avatar Nov 04 '23 15:11 xudongzheng

The only change I would like to propose is to name this rxHandler instead. Great work @xudongzheng

deadprogram avatar Nov 09 '23 06:11 deadprogram

Is that the only reason? Because handling things inside an interrupt is a pretty big hammer. If this is the only reason, a much better approach would be to make methods like ReadByte blocking (to avoid polling).

Thanks for the suggestion. Please see the latest commit fe186e971e58101b92b003de0c4e64d24e4bc787, which implements blocking Read() on both RP2040 UART and USB CDC ACM. If this implementation is preferred, I will go ahead and make the change for other boards as well, and also get rid of the RX callback code.

I opted to change the Read() behavior without changing the ReadByte() behavior since the error return value becomes meaningless if ReadByte() becomes blocking. However I can change ReadByte() along with the function return value if that's preferred.

xudongzheng avatar Nov 10 '23 21:11 xudongzheng

@xudongzheng I believe the related PR and Issue are as follows.

#2739 #2597 #2625 #2625

The essence of this PR is to make the Read() functions for UART and USBCDC more aligned with the expected Go io.Reader interface. In other words, we want to ensure that it does not return a value where n == 0 and err == nil. This will enable standard Go functions like fmt.Scanf to work smoothly.

In that case, I believe it should be implemented without using a channel. Additionally, this change will break compatibility with TinyGo. Documentation is also necessary for this, and it needs to be reviewed carefully.

Of course, it might also be an option to define a struct like UARTxxxx instead of UART to maintain compatibility.

https://pkg.go.dev/io#Reader Implementations of Read are discouraged from returning a zero byte count with a nil error, except when len(p) == 0. Callers should treat a return of 0 and nil as indicating that nothing happened; in particular it does not indicate EOF.

sago35 avatar Nov 11 '23 01:11 sago35

In that case, I believe it should be implemented without using a channel.

I don't quite see how this would be implemented without channels. For that reason, I initially opted for adding a callback rather than changing the Read() behavior - anyone who wants blocking reads can easily add a channel and write a wrapper to make reads block.

I think the overhead of channel should be pretty low, and it should be one or two at most since there won't be that many serial ports. Performance-wise, it works well enough as I was able to receive about 400KB/s on RP2040 over USB CDC ACM.

Additionally, this change will break compatibility with TinyGo. Documentation is also necessary for this, and it needs to be reviewed carefully.

I agree, though I would like a bit more clarity on the implementation direction before I start working on the documentation.

xudongzheng avatar Nov 11 '23 05:11 xudongzheng

Hi! Is there any progress on merging this PR?

lePereT avatar Mar 03 '24 09:03 lePereT