BrunoLevy / learn-fpga

Learning FPGA, yosys, nextpnr, and RISC-V
BSD 3-Clause "New" or "Revised" License
2.44k stars 236 forks source link

Interrupt handling, double triggering #104

Open spectrumero opened 1 year ago

spectrumero commented 1 year ago

First: It's entirely possible I'm barking up the wrong tree, I've only just started with FemtoRV, as it looks like a perfect fit for a project I'm embarking on.

To help me understand the core, I've made a very simple design with a short program running in block ram, and a timer that fires an interrupt. I've run into an issue with the interrupt:

  1. Timer hits target, set interrupt to 1
  2. ISR is called
  3. ISR code acknowledges interrupt by writing the timer's hardware register, so the interrupt line is reset
  4. do some stuff
  5. mret

What happens is the interrupt fires twice. Looking at the source code there's an interrupt_request_sticky register that (although I'm in my ISR and interrupts are disabled as a consequence until mret) gets set, because the interrupt is still 1 when interrupt_accepted (internal signal in the femtorv core) has gone back low.

I've always done interrupts with other cores (and physical CPUs) this way - interrupt controller holds INT high until software acknowledges it somewhere in the ISR.

Is this the wrong way to go about it? I can of course easily modify the FemtoRV core so that it doesn't have the interrupt_request_sticky register at all and then what I'm doing works just fine without the interrupt getting triggered twice... but as I said, I might be barking up the wrong tree and it is expected that the interrupt controller be designed in some other way.

Mecrisp commented 1 year ago

Hi, welcome to the group of FemtoRV experimenters!

The interrupt line in FemtoRV is designed to trigger on interrupt requests that last for exactly one clock cycle, for example you can wire a "count compare" or "counter overflow carry" directly to the interrupt request line.

Look at this snipplet from the processor:

always @(posedge clk) begin interrupt_request_sticky <= interrupt_request | (interrupt_request_sticky & ~interrupt_accepted); end

If your interrupt source is still active after the processor enters the handler which signals interrupt_accepted to clear the interrupt_request_sticky register, then a new pending interrupt will be waiting for the moment of reenabling interrupts when you finish your handler code. This design makes sure one does not miss a request pulse that happens while the processor already executes the handler.

Your solution is to pulse the interrupt_request line for one clock cycle only, exactly in the moment the timer fires. Technically, the interrupt_request_sticky logic IS the interrupt controller :-)

For your usual solution with a dedicated interrupt controller with multiple sources, holding interrupt_request until the software acks and clears the individual source, you can change the processor. I have not tried this but I believe that you can just delete a few lines and wire your interrupt controller directly to where the internal interrupt_request_sticky signal was.

Finally, the whole FemtoRV family members are designed to be easy to change, experiment and tinker with, and I can only encourage you to try! Have fun!

spectrumero commented 1 year ago

Thanks for the info.

Also to say I really like the FemtoRV, it seems very elegant in its simplicity and perfect for my ICE40 HX8K projects.