Hi! Yes, it is a little confusing at first, I agree.
There are 17 words of control registers, each word being 32 bits (unsigned int, or uint32_t). Each bit corresponds to a GPIO pin, so there are 17 control registers for each pin.
When changing one of the bits of a control register it is important not to change any of the other bits that you don’t want to change. Hence, a read-modify-write paradigm such as *input_en |= (0x1UL << pin)
would set the pinth bit of GPIO_INPUT_EN, where input_en = (uint32_t *) (0x10012000 + 0x04). There are some nice assembly instructions for doing this atomically. See amoand
, amoor
; and the conjugate pair of instructions lr
and sc
.
Here is a sketch below to see a graphical representation of the I/O pins:
Although some of the GPIO pins are not physically accessible, they all can be written to and read from. Thus, for example, one could use these hidden “pins” as single-bit memory locations.
The metal_gpio_enable_pinmux() basically does two things: first, it either sets or clears the appropriate bit of GPIO_IOF_SEL
depending on pinmux()'s third argument; second, it sets the appropriate bit of GPIO_IOF_EN
. In other words, either
GPIO_IOF_SEL &= ~(1 << pin)
or
GPIO_IOF_SEL |= (1 << pin)
and then followed by
GPIO_IOF_EN |= (1 << pin)
The metal_gpio_disable_pinmux() clears the appropriate bit of GPIO_IOF_EN
, as in
GPIO_IOF_EN &= ~(1 << pin)
These functions act through enable_io() and disable_io(), their source can be found at the path freedom-e-sdk/freedom-metal/src/drivers/sifive_gpio0.c
For each of the other metal_gpio_… functions in <metal/gpio.h>,
enable_input() and disable_input() effect changes on GPIO_INPUT_EN.
enable_output() and disable_output() control GPIO_OUTPUT_EN.
get_input_pin() reads from GPIO_INPUT_VAL, while
get_output_pin() reads from GPIO_OUTPUT_VAL.
set_pin() and clear_pin() effect changes on GPIO_OUTPUT_VAL.
toggle_pin() writes to GPIO_OUTPUT_VAL.
get_interrupt_id() reads from the GPIO_…IP status register,
config_interrupt() sets the GPIO…IE control register, and
clear_interrupt() writes to the GPIO…_IP status register.
It seems that there are no gpio_metal_… functions to access or control the registers GPIO_PUE, GPIO_DS, and GPIO_OUT_XOR.
When perusing the sources, it is helpful to note that the “common” header files are under freedom-e-sdk/freedom-metal/metal, whereas the actual source files are under freedom-e-sdk/freedom-metal/src. The “specific” header files are under, for example, freedom-e-sdk/bsp/sifive-hifive1-revb, with a “metal-” prefix on their filenames, which get built up by the makefiles during the installation process.