blob: 85b13c6c6c055c68165b65263f8ed8e92b9ece76 [file] [log] [blame] [view]
# GPIO HWIP Technical Specification
# Overview
This document specifies GPIO hardware IP functionality. This
module conforms to the [Comportable guideline for peripheral device
functionality](../../../doc/contributing/hw/comportability/README.md)
See that document for integration overview within the broader top
level system.
## Features
- 32 GPIO ports
- Configurable interrupt per GPIO for detecting rising edge, falling edge,
or active low/high input
- Two ways to update GPIO output: direct-write and masked (thread-safe) update
## Description
The GPIO block allows software to communicate through general purpose I/O
pins in a flexible manner. Each of 32 independent bits can be written
as peripheral outputs in two modes. Each of the 32 bits can be read
by software as peripheral inputs. How these peripheral inputs and
outputs are connected to the chip IO is not within the scope of this
document. See the Comportability Specification for peripheral IO options
at the top chip level.
In the output direction, this module provides direct 32b access to each
GPIO value using direct write. This mode allows software to control all
GPIO bits simultaneously. Alternately, this module provides masked writes
to half of the bits at a time, allowing software to affect the output
value of a subset of the bits without requiring a read-modify-write.
In this mode the user provides a mask of which of the 16 bits are to be
modified, along with their new value. The details of this mode are given
in the [Programmers Guide](#programmers-guide) below.
In the input direction, software can read the contents of any of the GPIO
peripheral inputs. In addition, software can request the detection of an
interrupt event for any of the 32 bits in a configurable manner. The choices
are positive edge, negative edge or level detection events. A noise
filter is available through configuration for any of the 32 GPIO inputs.
This requires the input to be stable for 16 cycles of the
module clock before the input register reflects the change and interrupt
generation is evaluated. Note that if the filter is enabled and the pin
is set to output then there will be a corresponding delay in a change
in output value being reflected in the input register.
See the Design Details section for more details on output, input, and
interrupt control.
# Theory of Operations
## Block Diagram
![GPIO Block Diagram](./doc/gpio_blockdiagram.svg)
The block diagram above shows the `DATA_OUT` and `DATA_OE` registers
managed by hardware outside of the auto-generated register file.
For reference, it also shows the assumed connections to pads in
the top level netlist.
## Hardware Interfaces
* [Interface Tables](data/gpio.hjson#interfaces)
## Design Details
### GPIO Output logic
![GPIO Output Diagram](./doc/gpio_output.svg)
The GPIO module maintains one 32-bit output register `DATA_OUT` with two
ways to write to it. Direct write access uses {{#regref gpio.DIRECT_OUT }}, and
masked access uses {{#regref gpio.MASKED_OUT_UPPER }} and
{{#regref gpio.MASKED_OUT_LOWER }}. Direct access provides full write and read
access for all 32 bits in one register.
For masked access the bits to modify are given as a mask in the upper
16 bits of the {{#regref gpio.MASKED_OUT_UPPER }} and
{{#regref gpio.MASKED_OUT_LOWER }} register write, while the data to write is
provided in the lower 16 bits of the register write. The hardware updates
`DATA_OUT` with the mask so that the modification is done without software
requiring a Read-Modify-Write.
Reads of masked registers return the lower/upper 16 bits of the `DATA_OUT`
contents. Zeros are returned in the upper 16 bits (mask field). To read
what is on the pins, software should read the {{#regref gpio.DATA_IN }} register.
(See [GPIO Input](#gpio-input) section below).
The same concept is duplicated for the output enable register `DATA_OE`.
Direct access uses {{#regref gpio.DIRECT_OE }}, and masked access is available
using {{#regref gpio.MASKED_OE_UPPER }} and {{#regref gpio.MASKED_OE_LOWER }}.
The output enable is sent to the pad control block to determine if the
pad should drive the `DATA_OUT` value to the associated pin or not.
A typical use pattern is for initialization and suspend/resume code to
use the full access registers to set the output enables and current output
values, then switch to masked access for both `DATA_OUT` and `DATA_OE`.
For GPIO outputs that are not used (either not wired to a pin output or
not selected for pin multiplexing), the output values are disconnected
and have no effect on the GPIO input, regardless of output enable values.
### GPIO Input
The {{#regref gpio.DATA_IN }} register returns the contents as seen on the
peripheral input, typically from the pads connected to those inputs. In the
presence of a pin-multiplexing unit, GPIO peripheral inputs that are
not connected to a chip input will be tied to a constant zero input.
The GPIO module provides optional independent noise filter control for
each of the 32 input signals. Each input can be independently enabled with
the {{#regref gpio.CTRL_EN_INPUT_FILTER }} (one bit per input). This 16-cycle
filter is applied to both the {{#regref gpio.DATA_IN }} register and
the interrupt detection logic. The timing for {{#regref gpio.DATA_IN }} is still
not instantaneous if {{#regref gpio.CTRL_EN_INPUT_FILTER }} is false as there is
top-level routing involved, but no flops are between the chip input and the
{{#regref gpio.DATA_IN }} register.
The contents of {{#regref gpio.DATA_IN }} are always readable and reflect the
value seen at the chip input pad regardless of the output enable setting from
DATA_OE. If the output enable is true (and the GPIO is connected to a
chip-level pad), the value read from {{#regref gpio.DATA_IN }} includes the
effect of the peripheral's driven output (so will only differ from DATA_OUT if
the output driver is unable to switch the pin or during the delay imposed
if the noise filter is enabled).
### Interrupts
The GPIO module provides 32 interrupt signals to the main processor.
Each interrupt can be independently enabled, tested, and configured.
Following the standard interrupt guidelines in the [Comportability
Specification](../../../doc/contributing/hw/comportability/README.md),
the 32 bits of the {{#regref gpio.INTR_ENABLE }} register determines whether the
associated inputs are configured to detect interrupt events. If enabled
via the various `INTR_CTRL_EN` registers, their current state can be
read in the {{#regref gpio.INTR_STATE }} register. Clearing is done by writing a
`1` into the associated {{#regref gpio.INTR_STATE }} bit field.
For configuration, there are 4 types of interrupts available per bit,
controlled with four control registers. {{#regref gpio.INTR_CTRL_EN_RISING }}
configures the associated input for rising-edge detection.
Similarly, {{#regref gpio.INTR_CTRL_EN_FALLING }} detects falling edge inputs.
{{#regref gpio.INTR_CTRL_EN_LVLHIGH }} and {{#regref gpio.INTR_CTRL_EN_LVLLOW }}
allow the input to be level sensitive interrupts. In theory an input can be
configured to detect both a rising and falling edge, but there is no hardware
assistance to indicate which edge caused the output interrupt.
**Note #1:** all inputs are sent through optional noise filtering before
being sent into interrupt detection. **Note #2:** all output interrupts to
the processor are level interrupts as per the Comportability Specification
guidelines. The GPIO module, if configured, converts an edge detection
into a level interrupt to the processor core.
# Programmers Guide
## Initialization
Initialization of the GPIO module includes the setting up of the interrupt
configuration for each GPIO input, as well as the configuration of
the required noise filtering. These do not provide masked access since
they are not expected to be done frequently.
```cpp
// enable inputs 0 and 1 for rising edge detection with filtering,
// inputs 2 and 3 for falling edge detection with filtering,
// input 4 for both rising edge detection (no filtering)
// and inputs 6 and 7 for active low interrupt detection
*GPIO_INTR_ENABLE = 0b11011111;
*GPIO_INTR_CTRL_EN_RISING = 0b00010011;
*GPIO_INTR_CTRL_EN_FALLING = 0b00011100;
*GPIO_INTR_CTRL_EN_LVLLOW = 0b11000000;
*GPIO_INTR_CTRL_EN_LVLHIGH = 0b00000000;
*GPIO_CTRL_EN_INPUT_FILTER = 0b00001111;
```
## Common Examples
This section below shows the interaction between the direct access
and mask access for data output and data enable.
```cpp
// assume all GPIO are connected to chip pads.
// assume a weak pullup on all pads, returning 1 if undriven.
printf("0x%x", *GPIO_DATA_IN); // 0xffffffff
*DIRECT_OUT = 0x11223344;
printf("0x%x", *GPIO_DIRECT_OUT); // 0x11223344
*DIRECT_OE = 0x00ff00ff;
printf("0x%x", *GPIO_DIRECT_OE); // 0x00ff00ff
// weak pullup still applies to undriven signals
printf("0x%x", *GPIO_DATA_IN); // 0xff22ff44
// read of direct_out still returns DATA_OUT contents
printf("0x%x", *GPIO_DIRECT_OUT); // 0x11223344
// try masked accesses to DATA_OUT
*GPIO_MASKED_OUT_LOWER = 0x0f0f5566
printf("0x%x", *GPIO_MASKED_OUT_LOWER); // 0x00003546
printf("0x%x", *GPIO_DIRECT_OUT); // 0x11223546
*GPIO_MASKED_OUT_UPPER = 0x0f0f7788
printf("0x%x", *GPIO_MASKED_OUT_UPPER); // 0x00001728
printf("0x%x", *GPIO_DIRECT_OUT); // 0x17283546
// OE still applies
printf("0x%x", *GPIO_DATA_IN); // 0xff28ff46
// manipulate OE
*GPIO_DIRECT_OE = 0xff00ff00;
printf("0x%x", *GPIO_DIRECT_OE); // 0xff00ff00
printf("0x%x", *GPIO_DATA_IN); // 0x17ff35ff
*GPIO_MASKED_OE_LOWER = 0x0f0f0f0f;
printf("0x%x", *GPIO_MASKED_OE_LOWER); // 0x00000f0f
printf("0x%x", *GPIO_DIRECT_OE); // 0xff000f0f
printf("0x%x", *GPIO_DATA_IN); // 0x17fff5f6
*GPIO_MASKED_OE_UPPER = 0x0f0f0f0f;
printf("0x%x", *GPIO_MASKED_OE_UPPER); // 0x00000f0f
printf("0x%x", *GPIO_DIRECT_OE); // 0x0f0f0f0f
printf("0x%x", *GPIO_DATA_IN); // 0xf7f8f5f6
```
## Interrupt Handling
This section below gives an example of how interrupt clearing works,
assuming some events have occurred as shown in comments.
```cpp
*INTR_ENABLE = 0x000000ff; // interrupts enabled GPIO[7:0] inputs
printf("0b%x", *GPIO_DATA_IN); // assume 0b00000000
printf("0b%x", *GPIO_INTR_STATE); // 0b00000000
*INTR_CTRL_EN_RISING = 0b00010001; // rising detect on GPIO[0], GPIO[4]
*INTR_CTRL_EN_FALLING = 0b00010010; // falling detect on GPIO[1], GPIO[4]
*INTR_CTRL_EN_LVLLOW = 0b00001100; // falling detect on GPIO[2], GPIO[3]
*INTR_CTRL_EN_LVLHIGH = 0b11000000; // falling detect on GPIO[6], GPIO[7]
// already detected intr[3,2] (level low)
printf("0b%b", *GPIO_INTR_STATE); // 0b00001100
// try and clear [3:2], fails since still active low
*GPIO_INTR_STATE = 0b00001100;
printf("0b%b", *GPIO_INTR_STATE); // 0b00001100
// EVENT: all bits [7:0] rising, triggers [7,6,4,0], [3,2] still latched
printf("0b%b", *GPIO_DATA_IN); // 0b11111111
printf("0b%b", *GPIO_INTR_STATE); // 0b11011101
// try and clear all bits, [7,6] still detecting level high
*GPIO_INTR_STATE = 0b11111111;
printf("0b%b", *GPIO_INTR_STATE); // 0b11000000
// EVENT: all bits [7:0] falling, triggers [4,3,2,1], [7,6] still latched
printf("0b%b", *GPIO_DATA_IN); // 0b00000000
printf("0b%b", *GPIO_INTR_STATE); // 0b11011110
// try and clear all bits, [3,2] still detecting level low
*GPIO_INTR_STATE = 0b11111111;
printf("0b%b", *GPIO_INTR_STATE); // 0b00001100
// write test register for all 8 events, trigger regardless of external events
*GPIO_INTR_TEST = 0b11111111;
printf("0b%b", *GPIO_INTR_STATE); // 0b11111111
// try and clear all bits, [3,2] still detecting level low
*GPIO_INTR_STATE = 0b11111111;
printf("0b%b", *GPIO_INTR_STATE); // 0b00001100
```
## Device Interface Functions (DIFs)
{{< dif_listing "sw/device/lib/dif/dif_gpio.h" >}}
## Register Table
* [Register Table](data/gpio.hjson#registers)