@bsvtgc everything seems well and good – I confirmed it with hardware and scope.
For interrupts to be sensed properly, the input enable bit for that pin must be set; otherwise, you won’t see any triggering. The INPUT_EN
(0x10012004) register enables both the reading of a pin’s level, and the sensing of its levels and/or edges.
If you are internally driving the pin, the corresponding output enable bit for that pin must also be set, in addition to the input enable bit. The OUTPUT_EN
(0x10012008) register enables the signal path out to the pin. Of course, if you are externally driving the pin, the output enable bit for that pin must not also be set.
Sketched below on Figure 9 of Chapter 16 of the Manual is the entire signal flow path, followed by the way I set up the code:
Route the path:
gpio_input_en |= (1UL << 21); // req'd for sensing interrupt
gpio_output_en |= (1UL << 21); // internal control, no external driving source connected
Enable the interrupt as you’ve done, and which I do in this order: mtvec
, PLIC_PRIORITY
(0=off, 1=lowest, 7=highest; written to 0x0C000000 + 4 * source_id), PLIC_ENABLE
(set bit source_id % 32; of 0x0C002000 + 4 * (source_id/32)), PLIC_THRESHOLD
(0=permit all, 7=mask all; written to 0x0C200000), interrupt source (i.e., CSR_MIE_MEIE) of mie
, and the interrupt enable (i.e., CSR_MSTATUS_MIE) of mstatus
.
Pick one, or more, sensing mode(s) as desired:
gpio_rise_ie = (1UL << 21);
gpio_fall_ie &= ~(1UL << 21);
gpio_high_ie &= ~(1UL << 21);
gpio_low_ie &= ~(1UL << 21);
Trigger the event:
while(1)
{
gpio_output_val |= (1UL << 21); // high
gpio_output_val &= ~(1UL << 21); // low
}
Handle the interrupt. In case of GPIO direct external mode (i.e., ‘GPIO’ mode, by which I mean 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: # 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
These are the definitions used above
#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))