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

Add halfway interrupts for SLINK interface #122

Closed
jeremyherbert opened this issue Jul 12, 2021 · 10 comments
Closed

Add halfway interrupts for SLINK interface #122

jeremyherbert opened this issue Jul 12, 2021 · 10 comments
Labels
enhancement New feature or request

Comments

@jeremyherbert
Copy link
Contributor

Describe the solution you'd like
The following text is in the documentation for the SLINK peripheral.

The stream interface provides two interrupts that are globally driven by the RX and TX link’s FIFO level status. If the FIFO of any TX link was full and becomes empty again, the TX interrupt fires. Accordingly, if the FIFO of any RX link was empty and a new data word appears in it, the RX interrupt fires.

Given that the TX and RX buffers can be quite long (up to 32k), it would be useful to have a halfway interrupt available so that more data can be computed.

An example scenario is when using a DAC at a fixed sample rate to generate some complex, non-periodic waveform where the samples are computed by the software; the TX empty interrupt is not enough, as this is too late. It means that the DAC will likely have to stall or have some conversions with no data.

Perhaps one other option would be to allow the user to specify the position of it, either by generics or at runtime.

Additional context
I wouldn’t necessarily expect this to be enabled by default.

@stnolting
Copy link
Owner

An example scenario is when using a DAC at a fixed sample rate to generate some complex, non-periodic waveform where the samples are computed by the software; the TX empty interrupt is not enough, as this is too late. It means that the DAC will likely have to stall or have some conversions with no data.

Good example! I did not think about this when implementing the SLINK. But it makes sense. So I agree that the IRQ conditions should be changed.

However, there is a problem: there is only one global TX interrupt. So if this interrupt indicates "TX FIFO half full" how does the software check which link's FIFO actually caused this interrupt? Right now there are only status flags that indicate if a certain TX is ready to send (so if the FIFO can take more data). There is the same problem with the global RX interrupt.

We could change the status flags or we could add more status flags that also indicate if a certain FIFO is half-full. 🤔

@jeremyherbert
Copy link
Contributor Author

Assuming that there would just be more status bits, what would be the current behaviour if, while the interrupt was being serviced, a separate tx halfway interrupt occurred? Are all of the status bits just ORed into the global interrupt line? If no interrupts would be missed, then I think just adding more status bits would be ok, splitting all of the status bits into their own 32 bit register perhaps?

I think having a configurable threshold rather than halfway is probably also useful for future proofing. I had a look at some STM32 data sheets with ethernet peripherals (where this functionality is common and often necessary) and they all had configurable levels with no fixed halfway interrupt. But this is obviously more costly in terms of registers as one would need to store the threshold.

@stnolting
Copy link
Owner

Assuming that there would just be more status bits, what would be the current behaviour if, while the interrupt was being serviced, a separate tx halfway interrupt occurred? Are all of the status bits just ORed into the global interrupt line?

Yes and no. The status flags are checked for edges and then they are OR-ed to generate the interrupt request. So if any FIFO fulfills the IRQ condition at any time an IRQ is generated.

If no interrupts would be missed, then I think just adding more status bits would be ok, splitting all of the status bits into their own 32 bit register perhaps?

Right now we have the following behavior: If a SLINK interrupt is being serviced right now, one more SLINK IRQ can trigger and will be queued until the current IRQ service handler has finished. I see that this might not be optimal. The current SLINK was build for hardware simplicity - the RX/TX interrupts just indicate "that something has happened" and it is up to the software to check which RX/TX links actually rang the interrupt.

I think having a configurable threshold rather than halfway is probably also useful for future proofing. I had a look at some STM32 data sheets with ethernet peripherals (where this functionality is common and often necessary) and they all had configurable levels with no fixed halfway interrupt. But this is obviously more costly in terms of registers as one would need to store the threshold.

I agree that this feature comes handy! But I think I would prefer to allow customization during a pre-synthesis constant where the default level is "half full". Furthermore, I think this is something to be configured via a constant in the package and not via a generic as it is a more specific option and the entity is already quite complex 😅

@jeremyherbert
Copy link
Contributor Author

I agree that this feature comes handy! But I think I would prefer to allow customization during a pre-synthesis constant where the default level is "half full". Furthermore, I think this is something to be configured via a constant in the package and not via a generic as it is a more specific option and the entity is already quite complex 😅

I have been thinking about this a little today, and I agree with you. I can't think of a realistic example where you would need to change this threshold at runtime. So pre-synthesis sounds like a good idea (I guess also powers of two to simplify the logic).

The interrupt problem is tricky. I believe the way this is handled on STM32s (as far as I can recall) is that the interrupts are level triggered, not edge triggered. So the interrupt will constantly fire unless the status register bit is cleared or the interrupt is masked off, and it is the responsibility of the software to somehow do one of these two things. If you perform some action to clear the status bit and then the event occurs again while the interrupt is running, it will run the interrupt a second time.

@stnolting
Copy link
Owner

I have been thinking about this a little today, and I agree with you. I can't think of a realistic example where you would need to change this threshold at runtime. So pre-synthesis sounds like a good idea (I guess also powers of two to simplify the logic).

👍

Do you think it is better to have per-link configurations or just one global configuration for the FIFO-level IRQ?

The interrupt problem is tricky. I believe the way this is handled on STM32s (as far as I can recall) is that the interrupts are level triggered, not edge triggered.

I tried to avoid level-sensitive interrupts for the CPU core. The CPU supports nested interrupts so a level-sensitive interrupt being high all the time could cause a deadlock (in the hardware!) if no care is taken by the software programmer.

How about this (this is quite close to your proposal):
If any FIFO (RX or TX) of any link reaches the "threshold" level (like half-full)...

  • an interrupt request to the CPU is generated
  • the SLINK hardware sets a "sticky" flag in its control register (there is one sticky flag for each FIFO, so a total of max 16 flags)

Then, the interrupt service handler has to...

  • check these sticky flags to determine which link/FIFO caused the interrupt
  • clear that sticky flag again in order to allow a new interrupt request from the according link/FIFO

@jeremyherbert
Copy link
Contributor Author

Yes, this approach sounds good to me.

@stnolting
Copy link
Owner

I thought about this again and I think that there is no need to provide an option to specify the FIFO fill level that triggers an interrupt. I think it is sufficient to always use "half-full" as trigger level. If an application requires a different level you could still add an external FIFO that asserts its "free" signal if its fill level has fallen below the custom level (in case of a TX link).

stnolting added a commit that referenced this issue Jul 14, 2021
SLINK now provides FIFO half-full interrupts
stnolting added a commit that referenced this issue Jul 14, 2021
added functions to check FIFO half-full fill level
@stnolting stnolting added the enhancement New feature or request label Jul 14, 2021
@stnolting
Copy link
Owner

I have reworked the SLINK interrupt system according to the discussion above.
-> 📚 SLINK

It was tested in simulation and with a very simple hardware setup, but I do not have a real application for verification of all aspects yet. I think the FOMU setups + a more sophisticated USB bridge (#102) might be a good real world test scenario.

@jeremyherbert
Copy link
Contributor Author

Looks good! I think USB data transfer is a good test case also.

@stnolting
Copy link
Owner

I think this can be closed.
Please open a new issue/discussion/PR if you find any bugs or if you have further ideas 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants