Skip to content
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

Shared uart isr #5600

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open

Shared uart isr #5600

wants to merge 11 commits into from

Conversation

Makuna
Copy link
Collaborator

@Makuna Makuna commented Jan 7, 2019

Issues:
#5555
Makuna/NeoPixelBus#119

Notes:
Sorry about the small format changes, but my editor when swapping tabs to spaces also does standard formating, like putting spaces after if and the like.
NeoPixelBus has a branch created that uses these changes
https://github.com/Makuna/NeoPixelBus/tree/SharedUartIsr

Since subscribe can not be automatically disable and enable interrupts due to other external uart setup must be done at the same time within the disabled interrupts, then unsubscribe should be made consistent

// uart_subscribeInterrupt & uart_unsubscribeInterrupt are not safe and must
// be called within ETS_UART_INTR_DISABLE()/ETS_UART_INTR_ENABLE() protection
//
Copy link
Collaborator

Choose a reason for hiding this comment

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

Other unsafe functions have _unsafe_ part of their name.
I think that is a good reminder for their users.
Or, why not calling INTR_EN/DISABLE() from inside these functions and remove that warning ?

Copy link
Collaborator Author

@Makuna Makuna Jan 8, 2019

Choose a reason for hiding this comment

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

If you glance at the use of subscribe inside uart.c, you will notice that a bunch of other stuff that needs to be done inside the INTR_EN/DISABLE sandwich. These other operations are similar but different from each use case. While unsubscribe does not; I decided to keep them consistent rather than only one require the sandwich. Keeping a balanced API.

I really don't like either direction (what I have and what you suggested). I am very open to other suggestions.

These are exposed, do we really want to mark them unsafe? While its self documenting, it seems inconsistent with any other API.

Copy link
Collaborator

Choose a reason for hiding this comment

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

  • Rename them _unsafe, add new ones calling the unsafe version in a INTR_EN/DISABLE sandwhich

  • too bad it's C, otherwise a parameter bool protect with a default value would do the job

  • keep as it is, and let's rely on users' intelligence for reading doc and comments

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please implement like bullet 1 above. The unsafe word in the function name is meant to indicate it's not interrupt-safe and it shouldn't be exported, if at all possible.
The unsafe functions are meant for resuse from other unsafe functions and/or for sandwich wrapping into safe versions.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not quite resolved. I'll try to explain again.
I said:

The unsafe word in the function name is meant to indicate it's not interrupt-safe and it shouldn't be exported

In this case, there could be undefined behavior if there is an IRQ in the middle of calling these functions.
Instead, mark the current implementations of these functions as static, and implement new functions without the _unsafe() word. Then export those in the .h. These exported versions call the _unsafe() functions inside a sandwich.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, it is ok to skip argument sanity checks in the _unsafe functions, but the safe versions should implement checks on the received args.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@devyte Maybe I was not clear enough. You can't wrap the subscribe as the site of where you call it has to be already wrapped with it due to several processes of setting up the uart hardware are required to be called within the same DISABLE/ENABLE. This is what I was pointing out by referencing the call spots withing UART.C. This same setup; but different based on needs of the caller is done externally by anyone needing to use uart ISR.
IF DISABLE/ENABLE were actually ref-count like entities, then it would be a good practice, as the ENABLE inside would only ref-count and not truly enable; but this is not the case.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you mean these unsafe functions need to be called by the user?
I think we need an example given this discussion, but something simple, e.g.: open a file and print it out to serial asynchronously a chunk at a time, or something along those lines. I think it would help to illustrate the usage, and how a user would interact with the functionality.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

YES, these two unsafe functions are called by the user. I pointed to my project that consumes these in the pull comments at the top.

Can I just point them to my code? Ok, its not simple ;-)

The real use of this is where you aren't doing simple serial, otherwise you would just use Serial. In general, using the hardware to talk to devices that take a serial stream; often where translation is done from data on the MCU as you send. Which means configuring the UART to your custom needs (using all those esoteric features to invert, turn off stop bits, etc) that are not exposed by Serial or even the current uart.h.

Copy link
Collaborator Author

@Makuna Makuna Jan 20, 2019

Choose a reason for hiding this comment

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

Here is a link to my code that does this specifically, copied below
https://github.com/Makuna/NeoPixelBus/blob/959cfac07b97e95e10fc71f311f81fa3e2757895/src/internal/NeoEsp8266UartMethod.cpp#L75

void NeoEsp8266UartInterruptContext::Attach()  {
    // Disable all interrupts
    ETS_UART_INTR_DISABLE();
    // Clear the RX & TX FIFOS
    const uint32_t fifoResetFlags = (1 << UCTXRST) | (1 << UCRXRST);
    USC0(_uartNum) |= fifoResetFlags;
    USC0(_uartNum) &= ~(fifoResetFlags);
    uart_subscribeInterrupt_unsafe(_uartNum, Isr, this);
    // Set tx fifo trigger. 80 bytes gives us 200 microsecs to refill the FIFO
    USC1(_uartNum) = (80 << UCFET);
    // Disable RX & TX interrupts. It maybe still enabled by uart.c in the SDK
    USIE(_uartNum) &= ~((1 << UIFF) | (1 << UIFE));
    // Clear all pending interrupts in UART1
    USIC(_uartNum) = 0xffff;
    // Reenable interrupts
    ETS_UART_INTR_ENABLE();
}

@devyte devyte self-requested a review January 8, 2019 02:15
cores/esp8266/uart.c Outdated Show resolved Hide resolved
cores/esp8266/uart.c Outdated Show resolved Hide resolved
uart_t* uart = (uart_t*)arg;
uint32_t usis = USIS(uart->uart_nr);

if (usis & (1 << UIFF))
Copy link
Collaborator

Choose a reason for hiding this comment

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

The sanity check if(uart == NULL || !uart->rx_enabled) is missing. It's supposed to guard against the ISR triggering by mistake, and in that case turn the interrupt off and keep going. I supposed it should really be an assert, because I think if it triggers by mistake it's really a programming error. Whether the original check or an assert, or something else, I think some sanity check here would be a good idea.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We have ASSERTs that turn off in a normal build?
I would agree with an ASSERT, but I disagree otherwise. This is an internal handler and not exposed, and all locations include an object. Param checks should be reserved for the outermost APIs. If hit such a check, then there is corruption.

Copy link
Collaborator

Choose a reason for hiding this comment

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

As I said, some sanity check. An assert is ok by me, as long as it's clear what it checks (i.e.: include comments).

Copy link
Collaborator Author

@Makuna Makuna Jan 20, 2019

Choose a reason for hiding this comment

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

BTW, the original check here was invalid. It would have dereferenced the null on lines above this check, this is why it was removed.
But let me be more specific in my request above. WHAT is the "assert" method to use? If I use "assert" I get a compile error of "undefined reference to `assert'".

int other_nr = (uart_nr + 1) % 2;
if (s_uartInterruptContext[other_nr].callback == NULL)
{
ETS_UART_INTR_ATTACH(uart_isr, s_uartInterruptContext);
Copy link
Collaborator

Choose a reason for hiding this comment

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

In uart_start_isr(), the current code calls ETS_UART_INTR_ATTACH() and then ETS_UART_INTR_ENABLE() right after. This new code does not.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See line 619, this is where it is enabled.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I had missed the significance of that, the disable-enable sandwich is moved there.
In that case, it seems to me that the semantics of uart_start_isr() changes, given that the isr isn't really started. I suggest a a change in the function name, and a comment for the function explaining the new behavior (i.e.: inits this, prepares that, blah, does not enable the interrupt yet, etc)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

renamed to uart_init_default_isr


// uart_subscribeInterrupt & uart_unsubscribeInterrupt are not safe and must
// be called within ETS_UART_INTR_DISABLE()/ETS_UART_INTR_ENABLE() protection
//
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not quite resolved. I'll try to explain again.
I said:

The unsafe word in the function name is meant to indicate it's not interrupt-safe and it shouldn't be exported

In this case, there could be undefined behavior if there is an IRQ in the middle of calling these functions.
Instead, mark the current implementations of these functions as static, and implement new functions without the _unsafe() word. Then export those in the .h. These exported versions call the _unsafe() functions inside a sandwich.

@devyte
Copy link
Collaborator

devyte commented Jan 23, 2019

@Makuna can you please take another look at this now? There are conflicts that need to be resolved, and some of your code could need adapting.
BTW, the conflicts are due to #5559 , which adds support for debugging with gdb while sharing the hw uart with the app. I'm not sure how/if that impacts the idea here.

@Makuna
Copy link
Collaborator Author

Makuna commented Jan 27, 2019

I will need to rewrite this some to address all the changes made on the other pull. Ignore this for now and once I create the new one I will close this.

@d-a-v d-a-v added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label May 25, 2019
@earlephilhower earlephilhower added the merge-conflict PR has a merge conflict that needs manual correction label Oct 1, 2019
@earlephilhower
Copy link
Collaborator

Thanks for your PR, but the core and libraries have changed enough that this PR now has a merge conflict.

Could you merge it manually with the latest core, so we can consider it for future releases?

@Makuna
Copy link
Collaborator Author

Makuna commented Oct 2, 2019

Last time I looked, the changes that were taken made my changes/solution IMPOSSIBLE. I really felt the debug stuff should not have gone in, it had inverted API use at the minimum and lots of poor assumptions. I felt rather put down since we had two pushes BEFORE it and we were the ones who had to react; TWO TIMES already.

Was that fixed?

@devyte
Copy link
Collaborator

devyte commented Oct 2, 2019

@Makuna I don't remember the status anymore, I got sidetracked too many times with stability pursuits. Can you please handle this so that we can look at it? We're currently trying to recover proposals that got put aside for various reasons.
If this is dependent on something else, please explain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merge-conflict PR has a merge conflict that needs manual correction waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants