/*! \page page_gpio_api GPIO API

\tableofcontents

\section xgpio_fpanel The Front Panel GPIO

All Generation-3 USRP offer an auxiliary GPIO connection
on the motherboard itself (independent of the daughterboards). These
GPIO pins are controlled directly by the FPGA, where they are controlled
by an ATR (Automatic Transmit / Receive). This allows them to be toggled
simultaneously with other radio-level changes (e.g., enabling or
disabling a TX or RX mixer).

For information on the X410 GPIO, see \ref page_x400_gpio_api

\subsection xgpio_fpanel_gpio X3x0 Front Panel GPIO

The GPIO port is not meant to drive big loads. You should not try to source more than 5mA per pin.

The +3.3V is for ESD clamping purposes only and not designed to deliver high currents.

\subsubsection xgpio_fpanel_conn Connector

\image html x3x0_gpio_conn.png "X3x0 GPIO Connectors"

\subsubsection xgpio_fpanel_pins Pin Mapping

-   Pin 1: +3.3V
-   Pin 2: Data[0]
-   Pin 3: Data[1]
-   Pin 4: Data[2]
-   Pin 5: Data[3]
-   Pin 6: Data[4]
-   Pin 7: Data[5]
-   Pin 8: Data[6]
-   Pin 9: Data[7]
-   Pin 10: Data[8]
-   Pin 11: Data[9]
-   Pin 12: Data[10]
-   Pin 13: Data[11]
-   Pin 14: 0V
-   Pin 15: 0V

\subsection egpio_internal_gpio E310/E312/E313 Internal GPIO

\subsubsection egpio_internal_conn Connector

\image html e3x0_gpio_conn.png "E31x GPIO Connector"

### Pin Mapping

- Pin 1:   +3.3V
- Pin 2:   I2C SCL (3.3 V)
- Pin 3:   Data[5]
- Pin 4:   I2C SDA (3.3 V)
- Pin 5:   Data[4]
- Pin 6:   Data[0]
- Pin 7:   Data[3]
- Pin 8:   Data[1]
- Pin 9:   0V
- Pin 10:  Data[2]

Pin 1 is connected to the shared +3.3V power rail and can be used to draw power.
The maximum current depends on the power used by the rest of the device, but
300-500 mA is generally safe. It is recommended to monitor the rail voltage
when drawing power from this pin.

\subsection e320_gpio_conn E320 External GPIO connector

### Front Panel GPIO Connections

| GPIO    | Mini HDMI (Type C)   |  HDMI (Type A)  |
|---------|----------------------|-----------------|
| Data[0] | Data 0+  - Pin 8     | Pin 7           |
| Data[1] | Data 0-  - Pin 9     | Pin 9           |
| Data[2] | Clock 0+ - Pin 11    | Pin 10          |
| Data[3] | Clock 0- - Pin 12    | Pin 12          |
| Data[4] | CEC      - Pin 14    | Pin 13          |
| Data[5] | SCL      - Pin 15    | Pin 15          |
| Data[6] | SDA      - Pin 16    | Pin 16          |
| Data[7] | Utility  - Pin 17    | Pin 14          |

\subsection xgpio_fpanel_atr Explaining ATR

ATR works by defining the value of the GPIO pins for certain states of
the radio. This is the "automatic" part of it. For example, you can tell
UHD that when the radio is transmitting and receiving (full duplex),
GPIO6 should be high, but when it is only transmitting, GPI06 should be
low. This state machine is set up using a series of GPIO attributes,
with paired values and a mask, which you will want to define for the
GPIO pins you intend to use. To set up the ATR, you use uhd::usrp::multi_usrp::set_gpio_attr().

-   **CTRL**: Is this pin controlled by ATR (automatic), or by manual
    control only?
-   **DDR**: "Data Direction Register" - defines whether or not a GPIO
    is an output or an input.
-   **OUT**: Manually set the value of a pin (only to be used in non-ATR
    mode).
-   **ATR_0X**: The status of the pins when the radio is **idle**.
-   **ATR_RX**: The status of the pins when the radio is **receiving** only.
-   **ATR_TX**: The status of the pins when the radio is **transmitting** only.
-   **ATR_XX**: The status of the pins when the radio is in **full-duplex** mode.

The counterpart to setting the ATR (the "getter"), is called
uhd::usrp::multi_usrp::get_gpio_attr().
It has the exact same attributes as above, and has
one more:

-   **READBACK**: Readback the GPIOs marked as inputs.

\subsection xgpio_fpanel_xample An Example

The front panel X3x0 GPIO bank is enumerated in the motherboard property
tree (`<mb_path>/gpio/FP0/\*`), the E31x internal GPIO bank as (`<mb_path>/gpio/INT0/\`)
and so are easily accessible through the standard uhd::usrp::multi_usrp UHD interface.

You can discover this using the uhd::usrp::multi_usrp::get_gpio_banks() function.
This will tell you that there is a GPIO bank on your X3x0, E320, or N3x0 called
"FP0" (for E31x this will be called "INT0").
This is the bank we want to set up.

Let's say we want to use GPIO6 for an external amp. We want it to be
automatically controlled by ATR as an output, and we want it to be high
when we are transmitting only, and low in all other cases. We are also using
GPIO4, which we want to control manually, as an output. We can set this
up with the following code:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    // set up our masks, defining the pin numbers
    #define AMP_GPIO_MASK   (1 << 6)
    #define MAN_GPIO_MASK   (1 << 4)

    #define ATR_MASKS       (AMP_GPIO_MASK | MAN_GPIO_MASK)

    // set up our values for ATR control: 1 for ATR, 0 for manual
    #define ATR_CONTROL     (AMP_GPIO_MASK)

    // set up the GPIO directions: 1 for output, 0 for input
    #define GPIO_DDR        (AMP_GPIO_MASK | MAN_GPIO_MASK)

    // assume an existing multi_usrp object, called "usrp"

    // now, let's do the basic ATR setup
    usrp->set_gpio_attr("FP0", "CTRL", ATR_CONTROL, ATR_MASKS);
    usrp->set_gpio_attr("FP0", "DDR", GPIO_DDR, ATR_MASKS);

    // let's manually set GPIO4 high
    usrp->set_gpio_attr("FP0", "OUT", MAN_GPIO_MASK, MAN_GPIO_MASK);

    // finally, let's set up GPIO6 as we described above
    usrp->set_gpio_attr("FP0", "ATR_0X", 0, AMP_GPIO_MASK);
    usrp->set_gpio_attr("FP0", "ATR_RX", 0, AMP_GPIO_MASK);
    usrp->set_gpio_attr("FP0", "ATR_TX", AMP_GPIO_MASK, AMP_GPIO_MASK);
    // usually, you would want to also make this pin go high when doing
    // full-duplex, but not in this example
    usrp->set_gpio_attr("FP0", "ATR_XX", 0, AMP_GPIO_MASK);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After the above code is run, the ATR in the FPGA will automatically
control GPIO6, as we have described, based on the radio state, and we
have direct manual control over GPIO4.

To modify the example to work with the E31x's internal GPIO bank, use the bank
name "INT0" instead of "FP0".

\subsection gpio_rfnoc_block Driving GPIO from an RFNoC Block

UHD allows GPIO pins to be driven or read by RFNoC blocks. For this your RFNoC block
needs additional wires that get later connected to the GPIO pins like so:

~~~
    // GPIO interface
    output wire [GPIO_WIDTH-1:0]  gpio_out,
    output wire [GPIO_WIDTH-1:0]  gpio_ddr,
    input wire  [GPIO_WIDTH-1:0]  gpio_in
~~~

This assumes you want to connect `GPIO_WIDTH`'s pins to the block where `GPIO_WIDTH` is a
parameter of your module. Drive `gpio_ddr[i]` to 1 if you want to use the pin `i` as
an output, 0 otherwise.

Your block yaml file needs to advertise its ability to connect to GPIO pins like so:

~~~
parameters:
    GPIO_WIDTH: ${ config.device.parameters['FP_GPIO_BANK_WIDTH'] }
io_ports:
    gpio:
        type: gpio
        drive: master
        parameters:
            width: "${parameters['GPIO_WIDTH']}"
~~~

If you block needs a fixed number of pins, it is totally fine to hard code that number in the
gpio section of the io_ports.

\subsubsection build_gpio_enable_rfnoc_block Build the bitstream

In the image core yaml file you have to connect your block with the pins of the device and build the bitfile.
Again, UHD supports connecting either all pins of a bank or a subset of it.

~~~
noc_blocks:
    my_gpio_block0:
        block_desc: 'my_gpio_block.yml'
connections:
    # either connect all available GPIOs to the RFNoC block
    - { srcblk: my_gpio_block0, srcport: gpio, dstblk: device,  dstport: fp_gpio }
    # or a subset, here drive pin 0 and 3 from the RFNoC block
    - { srcblk: my_gpio_block0, srcport: gpio[0], dstblk: device,  dstport: fp_gpio[0] }
    - { srcblk: my_gpio_block0, srcport: gpio[1], dstblk: device,  dstport: fp_gpio[3] }
~~~

\subsubsection use_gpio_enable_rfnoc_block_in_software Using the RFNoC Block in Software

You have to tell the UHD software which pins are controlled by your RFNoC block. This must match your
definitions in the image core yaml file. You can use either uhd::usrp::multi_usrp::set_gpio_src or
uhd::rfnoc::mb_controller::set_gpio_src depending on whether you are using the multi_usrp or
RFNoC API. Example:

~~~{.py}
    usrp = uhd.usrp.MultiUSRP("addr=<device IP>")
    bank = usrp.get_gpio_src_banks()[0]
    srcs = usrp.get_gpio_src(bank)
    # assumes you connected the subset like above
    srcs[0] = "USER_APP"
    srcs[3] = "USER_APP"
    usrp.set_gpio_src(bank, srcs)
~~~

*/
// vim:ft=doxygen:
