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
#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
//
// 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
// 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
# 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)
.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.
// 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
}
}