# \[PLIC\] Interrupt does not occur when disabling in the ISR before complete

**URL:** <https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075>\
**Category:** HiFive Unleashed\
**Created:** [March 13, 2023, 5:54am UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075 "2023-03-13T05:54:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![padmaraob](https://avatars.discourse-cdn.com/v4/letter/p/b9e5f3/32.png) [@padmaraob](https://forums.sifive.com/u/padmaraob)\
**Post date:** [March 13, 2023, 5:54am UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075/1 "2023-03-13T05:54:24Z")

</div>

Hi,

Point 1 - The Interrupt is not working  
Point 2 - The Interrupt is working

The SiFive PLIC is tested on the Microchip PolarFire SoC.

1). The Interrupt does not occur when disabling the interrupt before interrupt complete, **Is this expected behaviour with the SiFive PLIC?**  
With below Pseudo code, Claim the interrupt number and a call to the handler, the interrupt is disabled in the handler and there is no impact on the Interrupt Complete register when writing with the interrupt number, so the next interrupt(same) doesn’t occur even though the interrupt is enabled.

_ **PLIC External Interrupt ISR Pseudo Code** _\*\*\*\*\*\*\*\*\*\*  
void handle\_m\_ext\_interrupt(void)  
{  
int\_num = PLIC\_ClaimIRQ(); /\* get interrupt number by reading interrupt claim register _/  
ext\_irq\_handler(int\_num); /_ service the interrupt handler _/  
PLIC\_CompleteIRQ(int\_num); /_ Clear interrupt claim/complete register _/  
PLIC\_EnableIRQ(int\_num); /_ enable interrupt here or later _/  
}  
void ext\_irq\_handler(int\_num)  
{  
PLIC\_DisableIRQ(int\_num); /_ disable interrupt \*/  
}

* * *

2). The Interrupt occurs when the interrupt complete is cleared before disable the interrupt and getting the next interrupt.

_ **PLIC External Interrupt ISR Pseudo Code** _\*\*\*\*\*\*\*\*\*\*  
void handle\_m\_ext\_interrupt(void)  
{  
int\_num = PLIC\_ClaimIRQ(); /\* get interrupt number by reading interrupt claim register _/  
ext\_irq\_handler(int\_num); /_ service the interrupt service handler _/  
PLIC\_EnableIRQ(int\_num); /_ enable interrupt here or later _/  
}  
void ext\_irq\_handler(int\_num)  
{  
PLIC\_CompleteIRQ(int\_num); /_ Clear interrupt claim/complete register _/  
PLIC\_DisableIRQ(int\_num); /_ disable interrupt \*/  
}

* * *

Regards  
Padmarao

---

<div class="post-metadata">

**Author:** ![pds](https://avatars.discourse-cdn.com/v4/letter/p/8edcca/32.png) [@pds](https://forums.sifive.com/u/pds)\
**Post date:** [March 13, 2023, 4:34pm UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075/2 "2023-03-13T16:34:00Z")

</div>

Good question @padmaraob though I’m not familiar with the PolarFire SoC two thoughts might help you:

First, it’s not really necessary to disable and re-enable the PLIC interrupt source, because when an interrupt occurs this happens _automatically_ at the top-most level–the `MIE` bit in `mstatus` is automatically turned off.

Second, the PLIC interrupt pending bit (for the source you’ve claimed) is _automatically_ cleared during the claim process–by reading the Claim register as you’ve done. However, depending on the nature of your particular interrupt source, it may be necessary to manually clear that peripheral’s interrupt pending bit.

A simple and complete working example of PLIC, as well as CLIC and CLINT, is in the [start.s](https://github.com/psherman42/Demonstrating-MTVEC/blob/main/start.s) file of the **Demonstrating-MTVEC** repo. The `int_external_m_mode` function at line 304 shows how this happens:

```auto
int_external_m_mode:
  lui t0, %hi(PLIC_CLAIM) # read external interrupt source
  addi t0, t0, %lo(PLIC_CLAIM)
  lw t1, 0(t0) # act of reading clears pending bit
  sw t1, 0(t0) # signal claim complete
  addi t2, zero, PLIC_MAX_NUM_SOURCES
  bge t1, t2, int_external_unkn
  slli t1, t1, 1 # put on two byte boundary
  la t0, plic_vec_tab
  add t0, t0, t1
  jr t0

```

You may also find helpful the [SiFive Interrupt Cookbook](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf), especially Section 3.3 which states

> A successful claim will atomically clear the pending bit in the PLIC interrupt pending register, …

---

<div class="post-metadata">

**Author:** ![padmaraob](https://avatars.discourse-cdn.com/v4/letter/p/b9e5f3/32.png) [@padmaraob](https://forums.sifive.com/u/padmaraob)\
**Post date:** [March 14, 2023, 10:44am UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075/3 "2023-03-14T10:44:19Z")

</div>

The PolarFire SoC is based on SiFive’s U54-MC family and includes SiFive PLIC.

The disable and re-enable the PLIC interrupts are required to implement a two step interrupt processing (interrupt context followed by task context).

1). As per [start.s](https://github.com/psherman42/Demonstrating-MTVEC/blob/main/start.s) file. The `int_external_m_mode` function at line 307

```auto
  lw t1, 0(t0) # act of reading clears pending bit
  sw t1, 0(t0) # signal claim complete

```

Read the interrupt number from the interrupt claim register and immediately write it back to the interrupt complete register to clear and called the plic\_vec\_tab later.

Do we need to follow the same procedure? because in this scenario the interrupt disable and re-enable is working fine to get the next interrupt.  
Refer [interrupt-completion](https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc#8-interrupt-completion) -The interrupt can be completed only when the interrupt is enabled but not when disabled.

or

2). In the [SiFive Interrupt Cookbook](https://sifive.cdn.prismic.io/sifive/0d163928-2128-42be-a75a-464df65e04e0_sifive-interrupt-cookbook.pdf), Section 3.3.2?  
Claim the Interrupt, branch to the handler and complete the interrupt.

```auto
void machine_external_interrupt()
{
//get the highest priority pending PLIC interrupt
uint32_t int_num = plic.claim_comlete;
//branch to handler
plic_handler[int_num]();
//complete interrupt by writing interrupt number back to PLIC
plic.claim_complete = int_num;
}

```

---

<div class="post-metadata">

**Author:** ![pds](https://avatars.discourse-cdn.com/v4/letter/p/8edcca/32.png) [@pds](https://forums.sifive.com/u/pds)\
**Post date:** [March 15, 2023, 12:32am UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075/4 "2023-03-15T00:32:42Z")

</div>

Thanks @padmaraob the FU540 (E54 core, 4-hart) is rather similar to the FE310 (E31 core, 1-hart) which I use.

No, you don’t need to follow the same procedure exactly. You are free to `Complete` the interrupt process _ **at any time** _, whether before invoking your handler, as in example (1) above, or upon return from your handler, as in (2). It doesn’t matter. Just make sure to `Complete` the interrupt before executing your `mret` (or, `sret`, as appropriate) instruction and, thus, returning to main code to process other tasks.

Section 8 of the _RISC-V PLIC Specification_ as you point out details the internal operation of the interrupt completion process, which all happens automatically by the core whenever you write to the `CLAIM/COMPLETE` register. Nothing much is of concern here. The PLIC peripheral’s `CLAIM/COMPLETE` register is at offset 0x0020 n004, where `n` is the hart number 0, 1, 2, 3, etc. For example, Table 37 in the _FU540 Manual_ shows this. Both SoC’s FU540 and FE310 have the same base address, which is 0x0C00 0000, for their PLIC peripherals.

The only really significant statement in Section 8 is

> The PLIC does not check whether the completion ID is the same as the last claim ID for that target. If the completion ID does not match an interrupt source that is currently enabled for the target, the completion is silently ignored.

In other words, if you `COMPLETE` (i.e., write) a PLIC interrupt ID that never was `CLAIM`ed (i.e., read), then nothing happens, and after returning from the interrupt (with `m`/`s-ret` instruction) you’ll immediately re-enter the interrupt context with a pending interrupt ID. Similarly, if you `COMPLETE` an interrupt ID that never occurred, then nothing happens.

The reason that I use a look-up table (`plic_vec_tab`) is to cover all the possible claim-ID possibilities and select the one(s) that I want. You can also use a simpler approach to compare-and-branch for a single specific ID as well. Note that to get the table index position the interrupt ID is multiplied by two using `slli t1, t1, 1`, which must conform to the assembler directive alignment `.balign 2` and size of each table element’s instruction `j plic_...`; this is the case for the two-byte compressed form of each jump instruction. Of course, use `slli t1,t1,2` and `.balign 4` for full four-byte jump instructions if you make a table that way.

The individual claim-ID’s for all 53 interrupt sources in the FU540 are shown in Table 38 of Section 10.2 of the [FU540 Manual.](https://static.dev.sifive.com/FU540-C000-v1.0.pdf). Again, remember that some of these sources may need their interrupt pending bits to be manually cleared by the handler; see their individual descriptions as needed.

---

<div class="post-metadata">

**Author:** ![padmaraob](https://avatars.discourse-cdn.com/v4/letter/p/b9e5f3/32.png) [@padmaraob](https://forums.sifive.com/u/padmaraob)\
**Post date:** [March 15, 2023, 5:31am UTC](https://forums.sifive.com/t/plic-interrupt-does-not-occur-when-disabling-in-the-isr-before-complete/6075/5 "2023-03-15T05:31:34Z")

</div>

Thank You [Paul Sherman](https://forums.sifive.com/u/pds) for your support. 👍
