# Initialize and set up of GPIO pins

**URL:** <https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127>\
**Category:** HiFive1 Rev B\
**Created:** [May 15, 2023, 2:16pm UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127 "2023-05-15T14:16:20Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jonny040](https://avatars.discourse-cdn.com/v4/letter/j/59ef9b/32.png) [@Jonny040](https://forums.sifive.com/u/Jonny040)\
**Post date:** [May 15, 2023, 2:16pm UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/1 "2023-05-15T14:16:20Z")

</div>

Hey, I am quite new to using the HiFive1 rev b. I am using RISC-V MultiZone and want to implement a small window sensor software in one of the zones. Since I can’t figure out how to use the metal Freedom library with MultiZone I tried to set up the GPIO pin and the interrupt function with the given example. But I can’t seem to make it work. Can anybody help me and give me advice for the steps needed for the GPIO pin and interrupt to work?

Thanks in advance  
Jonny

---

<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:** [May 20, 2023, 4:10am UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/2 "2023-05-20T04:10:49Z")

</div>

Hi @Jonny040 I am not familiar with MultiZone but maybe these few notes can shed some light on your question.

The [interrupt handling procedural flow](https://forums.sifive.com/t/what-is-plic-interrupt-source-start-and-end-in-interrupt-source-mapping/5705/9) is a detailed discussion and good diagram here in the _forums_.

A very lean yet complete working example is [Demonstrating MTVEC](https://github.com/psherman42/Demonstrating-MTVEC) on my github site.

In general there’s only a few simple steps:  
_Assuming you’ve set up `mtvec` properly, enabled the CSR’s `mstatus.MIE` bit, and enabled the appropriate bit of GPIO\_IE_,

a) Read PLIC\_CLAIM to get the `source id` of the interrupt request – the 32 GPIO are id’s 8-39  
b) Write PLIC\_CLAIM to complete the request of that source  
c) Jump to desired code  
d) Write ‘1’ to appropriate bit of GPIO\_IP to clear the pending interrupt  
e) Exit with the `mret` instruction

You don’t need to clear the `mstatus.MIP` bit, because it is cleared _automatically_ when `mret` happens.

The specific method of clearing external interrupts varies depending on the nature of the external peripheral whose interrupt you enable; step (d) above is shown in case of GPIO. See the _procedural flow_ discussion above for clearing, for example, the UART, the SPI, the I2C, or the PWM periperals.

---

<div class="post-metadata">

**Author:** ![Jonny040](https://avatars.discourse-cdn.com/v4/letter/j/59ef9b/32.png) [@Jonny040](https://forums.sifive.com/u/Jonny040)\
**Post date:** [May 30, 2023, 1:42pm UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/3 "2023-05-30T13:42:18Z")

</div>

Hey, thanks for the great help. I think I got a little closer to the answer. Since your example is written in assembly code its quite hard for me to read or replicate though. Do you know of any examples in C?

---

<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:** [May 30, 2023, 4:59pm UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/4 "2023-05-30T16:59:39Z")

</div>

Surely @Jonny040 glad it helps. Here are a few sources for examples in C and also some general descriptions.

The [gpio-testbench.c](https://github.com/sifive/example-gpio-testbench/blob/master/gpio-testbench.c) is a small and self-contained example, with minimal dependencies. Do, however, keep the assembly code mentioned earlier in mind, to keep each bit of each register in mind and stay close to the true simplicity and beauty of the RISC-V ISA.

Explained in simple and concrete terms is the concept of the [PLIC interrupt source IDs](https://forums.sifive.com/t/what-is-plic-interrupt-source-start-and-end-in-interrupt-source-mapping/5705/10), which are otherwise vague, abstract, and highly obscure.

Some great and well-written general information is in the [SiFive Interrupt Cookbook](https://sifive.cdn.prismic.io/sifive/add53811-9616-4a92-917a-6767d7bde1e2_SiFive_Interrupt_Cookbook_v1p3.pdf) which you might find helpful. Note, however, that specific examples seem to work around the _device tree .dts_ configuration file structures for which this overall genrality, to me, seems to distract from a fundamental basic understanding of how CLIC, CLINT, and PLIC work in practice.

There is always the [SiFive Freedom Studio](https://static.dev.sifive.com/dev-tools/FreedomStudio_QuickStartGuide_v1p0.pdf); with a simple guide for helping to install it. Make sure to look at all the github repo’s of the `sifive` repository.

---

<div class="post-metadata">

**Author:** ![Jonny040](https://avatars.discourse-cdn.com/v4/letter/j/59ef9b/32.png) [@Jonny040](https://forums.sifive.com/u/Jonny040)\
**Post date:** [June 3, 2023, 1:31pm UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/5 "2023-06-03T13:31:18Z")

</div>

Thanks, I really appreciate the help. Do you now of any examples which is not using the Freedom Metal library? I could not make it work with MultiZone due to different toolchains…

---

<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:** [June 4, 2023, 3:12am UTC](https://forums.sifive.com/t/initialize-and-set-up-of-gpio-pins/6127/6 "2023-06-04T03:12:57Z")

</div>

Am still looking for a simple standalone example of GPIO with the PLIC in C. Meanwhile @Jonny040 here are a few sketches that might help you.

> **This is about the easiest way to configure a GPIO pin, in C**
>
> ```auto
> #define gpio_base 0x10012000
> #define gpio_input_val (*(volatile uint32_t *) (gpio_base + 0x00))
> #define gpio_input_en (*(volatile uint32_t *) (gpio_base + 0x04))
> #define gpio_output_en (*(volatile uint32_t *) (gpio_base + 0x08))
> #define gpio_output_val (*(volatile uint32_t *) (gpio_base + 0x0C))
> #define gpio_pue (*(volatile uint32_t *) (gpio_base + 0x10))
> #define gpio_ds (*(volatile uint32_t *) (gpio_base + 0x14))
> #define gpio_rise_ie (*(volatile uint32_t *) (gpio_base + 0x18))
> #define gpio_rise_ip (*(volatile uint32_t *) (gpio_base + 0x1C))
> #define gpio_fall_ie (*(volatile uint32_t *) (gpio_base + 0x20))
> #define gpio_fall_ip (*(volatile uint32_t *) (gpio_base + 0x24))
> #define gpio_high_ie (*(volatile uint32_t *) (gpio_base + 0x28))
> #define gpio_high_ip (*(volatile uint32_t *) (gpio_base + 0x2C))
> #define gpio_low_ie (*(volatile uint32_t *) (gpio_base + 0x30))
> #define gpio_low_ip (*(volatile uint32_t *) (gpio_base + 0x34))
> #define gpio_iof_en (*(volatile uint32_t *) (gpio_base + 0x38))
> #define gpio_iof_sel (*(volatile uint32_t *) (gpio_base + 0x3C))
> #define gpio_passthru_high_ie (*(volatile uint32_t *) (gpio_base + 0x44))
> #define gpio_passthru_low_ie (*(volatile uint32_t *) (gpio_base + 0x48))
> 
> ```

> **Input and output pins for the 48-QFN package of FE310, in C**
>
> ```auto
> //
> // ASIC PACKAGE pins
> // consult SoC datasheet for this information
> //
> #define FE310_48QFN_UNKN (0) # non-existent pin
> #define FE310_48QFN25_GPIO0_PWM0_0 (1UL << 0)
> #define FE310_48QFN26_GPIO1_PWM0_1 (1UL << 1)
> #define FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0 (1UL << 2)
> #define FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI (1UL << 3)
> #define FE310_48QFN29_GPIO4_SPI1_MISO (1UL << 4)
> #define FE310_48QFN31_GPIO5_SPI1_SCK (1UL << 5)
> #define FE310_48QFN33_GPIO9_SPI1_SS2 (1UL << 9)
> #define FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3 (1UL << 10)
> #define FE310_48QFN35_GPIO11_PWM2_1 (1UL << 11)
> #define FE310_48QFN36_GPIO12_PWM2_2 (1UL << 12)
> #define FE310_48QFN37_GPIO13_PWM2_3 (1UL << 13)
> #define FE310_48QFN38_GPIO16_UART0_RX (1UL << 16)
> #define FE310_48QFN39_GPIO17_UART0_TX (1UL << 17)
> #define FE310_48QFN40_GPIO18_UART1_TX (1UL << 18)
> #define FE310_48QFN42_GPIO20_PWM1_0 (1UL << 20)
> #define FE310_48QFN41_GPIO19_PWM1_1 (1UL << 19)
> #define FE310_48QFN43_GPIO21_PWM1_2 (1UL << 21)
> #define FE310_48QFN44_GPIO22_PWM1_3 (1UL << 22)
> #define FE310_48QFN45_GPIO23_UART1_RX (1UL << 23)
> 
> //
> // GPIO pins
> // IOF_EN=0
> //
> #define GPIO0 FE310_48QFN25_GPIO0_PWM0_0
> #define GPIO1 FE310_48QFN26_GPIO1_PWM0_1
> #define GPIO2 FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0
> #define GPIO3 FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI
> #define GPIO4 FE310_48QFN29_GPIO4_SPI1_MISO
> #define GPIO5 FE310_48QFN31_GPIO5_SPI1_SCK
> #define GPIO8 FE310_48QFN32_UNKN
> #define GPIO9 FE310_48QFN33_GPIO9_SPI1_SS2
> #define GPIO10 FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3
> #define GPIO11 FE310_48QFN35_GPIO11_PWM2_1
> #define GPIO12 FE310_48QFN36_GPIO12_PWM2_2
> #define GPIO13 FE310_48QFN37_GPIO13_PWM2_3
> #define GPIO16 FE310_48QFN38_GPIO16_UART0_RX
> #define GPIO17 FE310_48QFN39_GPIO17_UART0_TX
> #define GPIO18 FE310_48QFN40_GPIO18_UART1_TX
> #define GPIO20 FE310_48QFN42_GPIO20_PWM1_0
> #define GPIO19 FE310_48QFN41_GPIO19_PWM1_1
> #define GPIO21 FE310_48QFN43_GPIO21_PWM1_2
> #define GPIO22 FE310_48QFN44_GPIO22_PWM1_3
> #define GPIO23 FE310_48QFN45_GPIO23_UART1_RX
> 
> //
> // UART pins
> // IOF_SEL=0, IOF_EN=1
> //
> #define UART0_RX FE310_48QFN38_GPIO16_UART0_RX # input to SoC
> #define UART0_TX FE310_48QFN39_GPIO17_UART0_TX # output from SoC
> //
> #define UART1_RX FE310_48QFN45_GPIO23_UART1_RX # input to SoC
> #define UART1_TX FE310_48QFN40_GPIO18_UART1_TX # output from SoC
> 
> //
> // SPI pins
> // IOF_SEL=0, IOF_EN=1
> //
> #define SPI0_SCK FE310_48QFN31_GPIO5_SPI1_SCK
> #define SPI0_MOSI FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI
> #define SPI0_MISO FE310_48QFN29_GPIO4_SPI1_MISO
> #define SPI0_SS0 FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0
> #define SPI0_SS1 FE310_48QFN_UNKN
> #define SPI0_SS2 FE310_48QFN33_GPIO9_SPI1_SS2
> #define SPI0_SS3 FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3
> //
> #define SPI1_SCK FE310_48QFN31_GPIO5_SPI1_SCK
> #define SPI1_MOSI FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI
> #define SPI1_MISO FE310_48QFN29_GPIO4_SPI1_MISO
> #define SPI1_SS0 FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0
> #define SPI1_SS1 FE310_48QFN_UNKN
> #define SPI1_SS2 FE310_48QFN33_GPIO9_SPI1_SS2
> #define SPI1_SS3 FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3
> //
> #define SPI2_SCK FE310_48QFN31_GPIO5_SPI1_SCK
> #define SPI2_MOSI FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI
> #define SPI2_MISO FE310_48QFN29_GPIO4_SPI1_MISO
> #define SPI2_SS0 FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0
> #define SPI2_SS1 FE310_48QFN_UNKN
> #define SPI2_SS2 FE310_48QFN33_GPIO9_SPI1_SS2
> #define SPI2_SS3 FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3
> 
> //
> // PWM pins
> // IOF_SEL=1, IOF_EN=1
> //
> #define PWM0_0 FE310_48QFN25_GPIO0_PWM0_0
> #define PWM0_1 FE310_48QFN26_GPIO1_PWM0_1
> #define PWM0_2 FE310_48QFN27_GPIO2_PWM0_2_SPI1_SS0
> #define PWM0_3 FE310_48QFN28_GPIO3_PWM0_3_SPI1_MOSI
> //
> #define PWM1_0 FE310_48QFN42_GPIO20_PWM1_0
> #define PWM1_1 FE310_48QFN41_GPIO19_PWM1_1
> #define PWM1_2 FE310_48QFN43_GPIO21_PWM1_2
> #define PWM1_3 FE310_48QFN44_GPIO22_PWM1_3
> //
> #define PWM2_0 FE310_48QFN34_GPIO10_PWM2_0_SPI1_SS3
> #define PWM2_1 FE310_48QFN35_GPIO11_PWM2_1
> #define PWM2_2 FE310_48QFN36_GPIO12_PWM2_2
> #define PWM2_3 FE310_48QFN37_GPIO13_PWM2_3
> 
> //
> // I2C pins
> // IOF_SEL=0, IOF_EN=1
> //
> #define I2C0_SCK FE310_48QFN_UNKN
> #define I2C0_SDA FE310_48QFN_UNKN
> //
> #define I2C1_SCK FE310_48QFN_UNKN
> #define I2C1_SDA FE310_48QFN_UNKN
> 
> //
> // PCBA LAYOUT pins - alternate (physical) representation
> // consult schematic for this information
> //
> // _A side of PCBA is on the left (with button S1 at the bottom)
> #define LOFIVE_R1_PIN9_A FE310_48QFN_GPIO0_PWM0_0
> #define LOFIVE_R1_PIN10_A FE310_48QFN_GPIO1_PWM0_1
> #define LOFIVE_R1_PIN11_A FE310_48QFN_GPIO2_PWM0_2_SPI1_SS0
> #define LOFIVE_R1_PIN12_A FE310_48QFN_GPIO3_PWM0_3_SPI1_MOSI
> #define LOFIVE_R1_PIN13_A FE310_48QFN_GPIO4_SPI1_MISO
> #define LOFIVE_R1_PIN14_A FE310_48QFN_GPIO5_SPI1_SCK
> //
> // _B side of PCBA is on the right (with button S1 at the bottom)
> #define LOFIVE_R1_PIN2_B FE310_48QFN_GPIO23_UART1_RX // LoFive pin 27
> #define LOFIVE_R1_PIN3_B FE310_48QFN_GPIO22_PWM1_3 // LoFive pin 26
> #define LOFIVE_R1_PIN4_B FE310_48QFN_GPIO21_PWM1_2 // LoFive pin 25
> #define LOFIVE_R1_PIN5_B FE310_48QFN_GPIO20_PWM1_0 // LoFive pin 24
> #define LOFIVE_R1_PIN6_B FE310_48QFN_GPIO19_PWM1_1 // LoFive pin 23
> #define LOFIVE_R1_PIN7_B FE310_48QFN_GPIO18_UART1_TX // LoFive pin 22
> #define LOFIVE_R1_PIN8_B FE310_48QFN_GPIO17_UART0_TX // LoFive pin 21
> #define LOFIVE_R1_PIN9_B FE310_48QFN_GPIO16_UART0_RX // LoFive pin 20
> #define LOFIVE_R1_PIN10_B FE310_48QFN_GPIO13_PWM2_3 // LoFive pin 19
> #define LOFIVE_R1_PIN11_B FE310_48QFN_GPIO12_PWM2_2 // LoFive pin 18
> #define LOFIVE_R1_PIN12_B FE310_48QFN_GPIO11_PWM2_1 // LoFive pin 17
> #define LOFIVE_R1_PIN13_B, FE310_48QFN_GPIO10_PWM2_0_SPI1_SS3 // LoFive pin 16
> #define LOFIVE_R1_PIN14_B FE310_48QFN_GPIO9_SPI1_SS2 // LoFive pin 15
> 
> ```

> **Interrupt source ID's, in C**
>
> ```auto
> // interrupt enable and pending source IDs
> 
> // There is no PLIC interrupt source ‘0’, and the least significant (right-most) bit position #0 similarly reflects this.
> 
> // Lower register word
> 
> // AON : plic source id start=1 end=2
> 
> #define PLIC_AON_WDT 1 // (register bit position 1)
> #define PLIC_AON_RTC 2
> 
> // UART : plic source id start=3 end=4
> 
> #define PLIC_UART0 3
> #define PLIC_UART1 4
> 
> // SPI : plic source id start=5 end=7
> 
> #define PLIC_QSPI0 5
> #define PLIC_SPI1 6
> #define PLIC_SPI2 7
> 
> // GPIO : plic source id start=8 end=39
> 
> #define PLIC_GPIO0 8
> #define PLIC_GPIO1 9
> #define PLIC_GPIO2 10
> #define PLIC_GPIO3 11
> #define PLIC_GPIO4 12
> #define PLIC_GPIO5 13
> #define PLIC_GPIO6 14
> #define PLIC_GPIO7 15
> #define PLIC_GPIO8 16
> #define PLIC_GPIO9 17
> #define PLIC_GPIO10 18
> #define PLIC_GPIO11 19
> #define PLIC_GPIO12 20
> #define PLIC_GPIO13 21
> #define PLIC_GPIO14 22
> #define PLIC_GPIO15 23
> #define PLIC_GPIO16 24
> #define PLIC_GPIO17 25
> #define PLIC_GPIO18 26
> #define PLIC_GPIO19 27
> #define PLIC_GPIO20 28
> #define PLIC_GPIO21 29
> #define PLIC_GPIO22 30
> #define PLIC_GPIO23 31 // (register bit position 31)
> 
> // Upper register word
> 
> #define PLIC_GPIO24 32 // (register bit position 0)
> #define PLIC_GPIO25 33
> #define PLIC_GPIO26 34
> #define PLIC_GPIO27 35
> #define PLIC_GPIO28 36
> #define PLIC_GPIO29 37
> #define PLIC_GPIO30 38
> #define PLIC_GPIO31 39
> 
> // PWM : plic source id start=40 end=51
> 
> #define PLIC_PWM0_0 40
> #define PLIC_PWM0_1 41
> #define PLIC_PWM0_2 42
> #define PLIC_PWM0_3 43
> #define PLIC_PWM1_0 44
> #define PLIC_PWM1_1 45
> #define PLIC_PWM1_2 46
> #define PLIC_PWM1_3 47
> #define PLIC_PWM2_0 48
> #define PLIC_PWM2_1 49
> #define PLIC_pwm2_2 50
> #define PLIC_pwm2_3 51
> 
> // I2C : plic source id start=52 end=52
> 
> #define PLIC_I2C 52 // (register bit position 20)
> 
> ```

> **Put (or revise) these two lines in your start.s file**
>
> ```auto
> # set trap handler
> li t0, clint_trap_handler
> csrrw zero, mtvec, t0
> 
> ```

Here is the interrupt handler. The high level _Utility Functions_ are all callable from C. Simply copy/paste into the bottom of your `start.s` file, above any `.end` statement line, of course.

> **interrupt handler (put in start.s, near the end)**
>
> ```auto
> .equ CSR_MSTATUS_MPP, 0x00001800 # [12:11] machine prev priv mode
> .equ CSR_MSTATUS_SPP, 0x00000100 # [8] supervisor prev prev mode
> .equ CSR_MSTATUS_MPIE, 0x00000080 # [7] machine prev int enable
> .equ CSR_MSTATUS_SPIE, 0x00000020 # [5] supervisor prev int enable
> .equ CSR_MSTATUS_MIE, 0x00000008 # [3] machine int enable
> .equ CSR_MSTATUS_SIE, 0x00000002 # [1] supervisor int enable
> 
> .equ CSR_MIE_MEIE, 0x00000800 # [11] machine external int enable
> .equ CSR_MIE_SEIE, 0x00000200 # [9] supervisor external int enable
> .equ CSR_MIE_MTIE, 0x00000080 # [7] machine timer int enable
> .equ CSR_MIE_STIE, 0x00000020 # [5] supervisor int enable
> .equ CSR_MIE_MSIE, 0x00000008 # [3] machine software int enable
> .equ CSR_MIE_SSIE, 0x00000002 # [1] supervisor software int enable
> 
> .equ CSR_MIP_MEIP, 0x00000800 # [11] machine external int pending
> .equ CSR_MIP_SEIP, 0x00000200 # [9] supervisor external int pending
> .equ CSR_MIP_MTIP, 0x00000080 # [7] machine timer int pending
> .equ CSR_MIP_STIP, 0x00000020 # [5] supervisor int pending
> .equ CSR_MIP_MSIP, 0x00000008 # [3] machine software int pending
> .equ CSR_MIP_SSIP, 0x00000002 # [1] supervisor software int pending
> 
> .equ CSR_MCAUSE_EC, 0x0000003F # 0x3ff ??? # [9:0] exception code
> 
> .equ CLINT_BASE, 0x02000000
> .equ CLINT_MSIP_BASE, (CLINT_BASE + 0x0000)
> .equ CLINT_MTIMECMP_BASE, (CLINT_BASE + 0x4000)
> .equ CLINT_MTIME, (CLINT_BASE + 0xbff8)
> 
> .equ CLINT_MSIP_HART0, (CLINT_MSIP_BASE + 8 * 0)
> .equ CLINT_MSIP_HART1, (CLINT_MSIP_BASE + 8 * 1)
> .equ CLINT_MSIP_HART2, (CLINT_MSIP_BASE + 8 * 2)
> .equ CLINT_MSIP_HART3, (CLINT_MSIP_BASE + 8 * 3)
> 
> .equ CLINT_MTIMECMP_HART0, (CLINT_MTIMECMP_BASE + 8 * 0)
> .equ CLINT_MTIMECMP_HART1, (CLINT_MTIMECMP_BASE + 8 * 1)
> .equ CLINT_MTIMECMP_HART2, (CLINT_MTIMECMP_BASE + 8 * 2)
> .equ CLINT_MTIMECMP_HART3, (CLINT_MTIMECMP_BASE + 8 * 3)
> 
> .equ PLIC_BASE, 0x0C000000
> .equ PLIC_PRIO, (PLIC_BASE + 0x000000) # 4 * CLAIM number
> .equ PLIC_PEND, (PLIC_BASE + 0x001000) # 64-bit value
> .equ PLIC_ENA, (PLIC_BASE + 0x002000) # 64-bit value
> .equ PLIC_THRESHOLD, (PLIC_BASE + 0x200000)
> .equ PLIC_CLAIM, (PLIC_BASE + 0x200004)
> 
> .equ PLIC_MAX_NUM_SOURCES, 52 # FE310-G002
> 
> ###############################################
> ##
> ## clint trap handler
> ##
> 
> .balign 8 # required 64-bit alignment for mtvec
> clint_trap_handler: .global clint_trap_handler
> csrr t0, mcause # read trap cause
> andi t1, t0, CSR_MCAUSE_EC # isolate exception code
> #
> addi t2, zero, 16 # CLINT_MAX_NUM_SOURCES
> bge t1, t2, clint_trap_unkn
> #
> slli t1, t1, 2 # put on four byte (far jump) boundary
> #
> bgez t0, clint_trap_exception # branch if not an interrupt
> #
> 
> clint_trap_interrupt:
> la t0, clint_int_vec_tab # interrupt (asynchronous) source: cause < 0
> add t0, t0, t1
> jr t0
> 
> clint_trap_exception:
> j .
> 
> clint_trap_unkn:
> j . # unknown cause - loop forever, for now
> 
> #
> # interrupt (asynchronous) sources
> #
> 
> .option push
> .option norvc
> .balign 4
> clint_int_vec_tab: # each must be four byte (far jump) elements, or else
> j . # source id 0
> j . # source id 1
> j . # source id 2
> j . # source id 3 (middle async prio)
> j . # source id 4
> j . # source id 5
> j . # source id 6
> j . # source id 7 (lowest async prio)
> j int_external_m_mode # source id 8 (int_external_u_mode, really)
> j int_external_m_mode # source id 9 (int_external_u_mode, really)
> j int_external_m_mode # source id 10 (int_external_u_mode, really)
> j int_external_m_mode # source id 11 (highest async prio)
> j . # source id 12
> j . # source id 13
> j . # source id 14
> j . # source id 15
> .option pop
> 
> ###############################################
> ##
> ## plic trap handler
> ##
> 
> ##
> ## external interrupt (asynchronous) source
> ##
> 
> .balign 4 # required 64-bit alignment for mtvec
> int_external_m_mode: .global 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
> #
> addi sp, sp, -16 # alloc stack frame
> sw ra, 12(sp) # save return addr
> sw t1, 8(sp) # save int source id
> sw t0, 4(sp) # save claim/complete reg
> #
> addi t2, zero, PLIC_MAX_NUM_SOURCES
> bge t1, t2, int_external_unkn
> #
> 
> slli t1, t1, 2 # put on four byte (far jump) boundary
> la t0, plic_vec_tab
> add t0, t0, t1
> jalr t0
> #
> lw t0, 4(sp) # restore claim/complete reg
> lw t1, 8(sp) # restore int source id
> lw ra, 12(sp) # restore return addr
> addi sp, sp, 16 # dealloc stack frame
> #
> sw t1, 0(t0) # signal claim complete
> #
> mret
> 
> int_external_unkn:
> j . # loop forever, for now
> 
> .option push
> .option norvc
> .balign 4
> plic_vec_tab: # each must be four byte (far jump) elements, or else
> j . # source id 0
> j . # source id 1
> j . # source id 2
> j . # source id 3
> j . # source id 4
> j . # source id 5
> j . # source id 6
> j . # source id 7
> j plic_gpio0 # source id 8
> j plic_gpio1 # source id 9
> j plic_gpio2 # source id 10
> j plic_gpio3 # source id 11
> j plic_gpio4 # source id 12
> j plic_gpio5 # source id 13
> j plic_gpio6 # source id 14
> j plic_gpio7 # source id 15
> j plic_gpio8 # source id 16
> j plic_gpio9 # source id 17
> j plic_gpio10 # source id 18
> j plic_gpio11 # source id 19
> j plic_gpio12 # source id 20
> j plic_gpio13 # source id 21
> j plic_gpio14 # source id 22
> j plic_gpio15 # source id 23
> j plic_gpio16 # source id 24
> j plic_gpio17 # source id 25
> j plic_gpio18 # source id 26
> j plic_gpio19 # source id 27
> j plic_gpio20 # source id 28
> j plic_gpio21 # source id 29
> j plic_gpio22 # source id 30
> j plic_gpio23 # source id 31
> j plic_gpio24 # source id 32 
> j plic_gpio25 # source id 33
> j plic_gpio26 # source id 34 
> j plic_gpio27 # source id 35 
> j plic_gpio28 # source id 36 
> j plic_gpio29 # source id 37
> j plic_gpio30 # source id 38
> j plic_gpio31 # source id 39 
> j . # source id 40
> j . # source id 41
> j . # source id 42
> j . # source id 43
> j . # source id 44
> j . # source id 45
> j . # source id 46
> j . # source id 47
> j . # source id 48
> j . # source id 49
> j . # source id 50
> j . # source id 51
> j . # source id 52
> .option pop
> 
> # Handle an interrupt
> # In case of GPIO direct external mode (i.e., ‘GPIO’ mode, meaning non-IOF
> # functions, when the IOF_EN bit for that pin is set to 0), you must manually
> # clear the pending interrupt before the handler returns.
> # Otherwise, you will see a never-ending stream of interrupts back to the
> # same handler. For the GPIO block, clear the _IP pending bits by setting
> # them, rather than clearing them.
> 
> # Some time during the handling process, either before or after your code,
> # you need to CLAIM the interrupt, by reading the value from 0x0C200004,
> # and writing it back to the same register. I read the claim value before my
> # code, save it on the stack, and write it back after my code,
> # as suggested in the RISC-V documentation.
> 
> .equ GPIO_BASE, 0x10012000
> .equ GPIO_RISE_IP, 0x1C
> .equ GPIO_FALL_IP, 0x24
> .equ GPIO_HIGH_IP, 0x2C
> .equ GPIO_LOW_IP, 0x34
> 
> plic_gpio21: .global plic_gpio21 # plic source id 29
> # your stuff goes here ...
> 
> # set to '1' the corresponding interrupt pending bit
> # to clear the trigger mode(s) you have enabled
> lui t0, %hi(GPIO_BASE)
> addi t2, t2, 1
> slli t2, t2, 21 # 2 ^ 21
> lw t1, GPIO_RISE_IP(t0)
> or t1, t1, t2
> sw t1, GPIO_RISE_IP(t0)
> 
> lw t1, GPIO_FALL_IP(t0)
> or t1, t1, t2
> sw t1, GPIO_FALL_IP(t0)
> 
> lw t1, GPIO_HIGH_IP(t0)
> or t1, t1, t2
> sw t1, GPIO_HIGH_IP(t0)
> 
> lw t1, GPIO_LOW_IP(t0)
> or t1, t1, t2
> sw t1, GPIO_LOW_IP(t0)
> 
> ret
> 
> # other pin(s) if you want to implement them as above
> plic_gpio0: .weak plic_gpio0
> plic_gpio1: .weak plic_gpio1
> plic_gpio2: .weak plic_gpio2
> plic_gpio3: .weak plic_gpio3
> plic_gpio4: .weak plic_gpio4
> plic_gpio5: .weak plic_gpio5
> plic_gpio6: .weak plic_gpio6
> plic_gpio7: .weak plic_gpio7
> plic_gpio8: .weak plic_gpio8
> plic_gpio9: .weak plic_gpio9
> plic_gpio10: .weak plic_gpio10
> plic_gpio11: .weak plic_gpio11
> plic_gpio12: .weak plic_gpio12
> plic_gpio13: .weak plic_gpio13
> plic_gpio14: .weak plic_gpio14
> plic_gpio15: .weak plic_gpio15
> plic_gpio16: .weak plic_gpio16
> plic_gpio17: .weak plic_gpio17
> plic_gpio18: .weak plic_gpio18
> plic_gpio19: .weak plic_gpio19
> plic_gpio20: .weak plic_gpio20
> plic_gpio21: .weak plic_gpio21
> plic_gpio22: .weak plic_gpio22
> plic_gpio23: .weak plic_gpio23
> plic_gpio24: .weak plic_gpio24
> plic_gpio25: .weak plic_gpio25
> plic_gpio26: .weak plic_gpio26
> plic_gpio27: .weak plic_gpio27
> plic_gpio28: .weak plic_gpio28
> plic_gpio29: .weak plic_gpio29
> plic_gpio30: .weak plic_gpio30
> plic_gpio31: .weak plic_gpio31
> 
> ###############################################
> ##
> ## user mode utility functions - callable from C
> ##
> 
> #
> # void disable_interrupts (void);
> #
> 
> disable_interrupts: .globl disable_interrupts
> csrrci zero, mstatus, (CSR_MSTATUS_MIE | CSR_MSTATUS_SIE) # disable interrupts
> #
> lui t0, %hi(CSR_MIE_MEIE | CSR_MIE_MTIE | CSR_MIE_MSIE) # disable interrupt sources
> addi t0, t0, %lo(CSR_MIE_MEIE | CSR_MIE_MTIE | CSR_MIE_MSIE) # disable interrupt sources
> csrrc zero, mie, t0
> #
> ret
> 
> #
> # void disable_external_interrupts (void);
> #
> 
> disable_external_interrupts: .global disable_external_interrupts
> lui t0, %hi(PLIC_ENA)
> addi t0, t0, %lo(PLIC_ENA)
> sw zero, 0(t0) # 1 .. 31
> sw zero, 4(t0) # 32 .. 52
> 
> lui t0, %hi(CSR_MIE_MEIE) # disable external interrupt source
> addi t0, t0, %lo(CSR_MIE_MEIE)
> csrrc zero, mie, t0
> #
> ret
> 
> # INPUT:
> # a0 - plic_source_id
> # OUTPUT:
> # t0 - address of plic_ena register
> # t2 - desired bit position
> # USES:
> # t1
> plic_enable_bit:
> lui t0, %hi(PLIC_ENA)
> addi t0, t0, %lo(PLIC_ENA)
> addi t1, zero, 32
> #
> divu t2, a0, t1 # t2 = a0 / 32
> slli t2, t2, 2 # t2 = 4 * t2
> add t0, t0, t2
> #
> remu t2, a0, t1 # t2 = a2 % 32
> addi t1, zero, 1
> sll t2, t1, t2 # t2 = 2 ^ t2
> #
> ret
> 
> #
> # void disable_external_interrupt (uint8_t plic_source_id);
> #
> # a0 - plic_source_id: 1=aon_wdt, 2=aon_rtc, 3-4=uart, 5-7=spi, 8-39=gpio, 40-51=pwm, ..., 52=i2c
> #
> 
> disable_external_interrupt: .global disable_external_interrupt
> addi sp, sp, -16
> sw ra, 12(sp)
> #
> jal plic_enable_bit # a0=id --> t0=addr(reg), t2=packed-bit
> #
> lw t1, 0(t0)
> not t2, t2
> and t1, t1, t2 # clear
> sw t1, 0(t0)
> #
> lw ra, 12(sp)
> addi sp, sp, 16
> #
> ret
> 
> #
> # void enable_external_interrupt (uint8_t plic_source_id, uint8_t plic_source_prio);
> #
> # a0 - plic_source_id: 1=aon_wdt, 2=aon_rtc, 3-4=uart, 5-7=spi, 8-39=gpio, 40-51=pwm, ..., 52=i2c
> # a1 - plic_source_prio: 0=off, 1=lowest, ..., 7=highest
> #
> 
> enable_external_interrupt: .global enable_external_interrupt
> addi sp, sp, -16
> sw ra, 12(sp)
> #
> jal prio_external_interrupt # a0=id, a1=prio
> 
> jal plic_enable_bit # a0=id --> t0=addr(reg), t2=packed-bit
> #
> lw t1, 0(t0)
> or t1, t1, t2 # set
> sw t1, 0(t0)
> 
> lui t0, %hi(PLIC_THRESHOLD)
> addi t0, t0, %lo(PLIC_THRESHOLD) # define external interrupt threshold level
> addi t1, zero, 0 # 0=permit-all-nonzero-prio, ..., 7=mask-all-prio
> sw t1, 0(t0)
> #
> lui t0, %hi(CSR_MIP_MEIP)
> addi t0, t0, %lo(CSR_MIP_MEIP) # clear external interrupt pending bit
> csrrc zero, mip, t0
> #
> lui t0, %hi(CSR_MIE_MEIE)
> addi t0, t0, %lo(CSR_MIE_MEIE) # enable external interrupt sources
> csrrs zero, mie, t0
> #
> csrrsi zero, mstatus, CSR_MSTATUS_MIE # enable interrupts
> #
> lw ra, 12(sp)
> addi sp, sp, 16
> #
> ret
> 
> #
> # void prio_external_interrupt (uint8_t plic_source_id, uint8_t plic_source_prio);
> #
> # a0 - plic_source_id: 1=aon_wdt, 2=aon_rtc, 3-4=uart, 5-7=spi, 8-39=gpio, 40-51=pwm, ..., 52=i2c
> # a1 - plic_source_prio: 0=off, 1=lowest, ..., 7=highest
> #
> 
> prio_external_interrupt: .global prio_external_interrupt
> lui t0, %hi(PLIC_PRIO)
> addi t0, t0, %lo(PLIC_PRIO)
> slli t1, a0, 2 # 4 * plic_source_id
> add t0, t0, t1
> sw a1, 0(t0) # plic_source_prio
> #
> ret
> 
> #
> # void wait_for_interrupt (void);
> #
> 
> wait_for_interrupt: .globl wait_for_interrupt
> wfi
> #
> ret
> 
> ```

You can configure and generate an interrupt on GPIO like this, for example, with GPIO21 as an _input_ and only _rising edge_ interrupt. Notice **carefully** that `gpio_XXX` functions take input and output pin numbers, while the `XXX_external_interrupt` functions take interrupt source ID numbers.

```auto
// function prototypes and forward declarations
void disable_interrupts (void); // lives in, e.g., start.s
void disable_external_interrupts (void); // lives in, e.g., start.s
void disable_external_interrupt (uint8_t plic_source_id); // lives in, e.g., start.s
void enable_external_interrupt (uint8_t plic_source_id, uint8_t plic_source_prio); // lives in, e.g., start.s
void prio_external_interrupt (uint8_t plic_source_id, uint8_t plic_source_prio); // lives in, e.g., start.s
void wait_for_interrupt (void); // lives in, e.g., start.s
// where
// plic_source_id: 1=aon_wdt, 2=aon_rtc, 3-4=uart, 5-7=spi, 8-39=gpio, 40-51=pwm, ..., 52=i2c
// plic_source_prio: 0=off, 1=lowest, ..., 7=highest

void main()
{
  disable_interrupts();
  disable_external_interrupts();

  // route the path - these functions take input and output PIN numbers

  gpio_iof_en &= ~(1UL << GPIO21); // general (direct) function i/o mode
//gpio_iof_sel &= ~(1UL << GPIO21); // special function i/o #0
//gpio_iof_sel |= (1UL << GPIO21); // special function i/o #1
//gpio_iof_en |= (1UL << GPIO21); // special function i/o modes
  gpio_output_en |= (1UL << GPIO21); // optional internal control, when no external driving source connected
  gpio_input_en |= (1UL << GPIO21); // req'd for sensing interrupt
  gpio_pue &= ~(1UL << GPIO21); // input pull-up (0=dis, 1=ena)

  // pick one, or more, sensing mode(s) as desired:
  gpio_rise_ie = (1UL << GPIO21);
  gpio_fall_ie &= ~(1UL << GPIO21);
  gpio_high_ie &= ~(1UL << GPIO21);
  gpio_low_ie &= ~(1UL << GPIO21);

  // enable the interrupt - these functions all take interrupt source ID numbers

  enable_external_interrupt( PLIC_GPIO21, 5 ); //gpio21=29, prio #5

  // trigger the event

  while(1)
  {
    gpio_output_val |= (1UL << GPIO21_PWM2_2); // high
    gpio_output_val &= ~(1UL << GPIO21_PWM2_2); // low
  }
}

```
