|
|
|
# Standalone Peripheral Drivers
|
|
|
|
|
|
|
|
- [Board Support Packages and Drivers](#board-support-packages-and-drivers)
|
|
|
|
- [The Xilinx Parameters File](#the-xilinx-parameters-file)
|
|
|
|
- [Processing System GPIO Examples](#processing-system-gpio-examples)
|
|
|
|
- [GPIO Polled Example](#gpio-polled-example)
|
|
|
|
- [GPIO Interrupt Example](#gpio-interrupt-example)
|
|
|
|
|
|
|
|
## Board Support Packages and Drivers
|
|
|
|
|
|
|
|
**NOTE:** we are using the Cortex-A53, but all of this content would be valid if we had selected a Cortex-R5 core for our standalone domain.
|
|
|
|
|
|
|
|
If we take a look to the Board Support Package for the psu_cortexa53_0 domain, we can see an entry with the list of available **Peripheral Drivers** in our *ultra96v2_standalone_platform*:
|
|
|
|
|
|
|
|
![vitis_standalone_peripheral_drivers](uploads/89b2e5e4901b92dc428433a7758c5634/vitis_standalone_peripheral_drivers.png)
|
|
|
|
|
|
|
|
If we click in the entry, we will get access for each of the peripheral devices details, including:
|
|
|
|
- Associated driver
|
|
|
|
- Driver Documentation
|
|
|
|
- Importable Examples
|
|
|
|
|
|
|
|
The list of drivers is **created automatically from the list of devices in our Vivado design** when creating the platform, and can be edited in the **Modify BSP Settings** menu.
|
|
|
|
|
|
|
|
## The Xilinx Parameters File
|
|
|
|
|
|
|
|
In every standalone BSP, the **low level definitions for all of the supported peripherals**, are contained in the *xparameters.h* file, including:
|
|
|
|
- Peripheral Name
|
|
|
|
- Address Range
|
|
|
|
- Interrupt lanes
|
|
|
|
|
|
|
|
Before going forward, we should take a look to the contents of the *xparameters.h* file in the *ultra96v2_standalone_platform*, e.g.:
|
|
|
|
- **Sources for the platform**:
|
|
|
|
- psu_cortexa53_0
|
|
|
|
- standalone_domain
|
|
|
|
- bsp
|
|
|
|
- psu_cortexa53_0
|
|
|
|
- include
|
|
|
|
- *xparameters.h*
|
|
|
|
- **Exported platform**:
|
|
|
|
- export
|
|
|
|
- ultra96v2_standalone_platform
|
|
|
|
- sw
|
|
|
|
- ultra96v2_standalone_platform
|
|
|
|
- standalone_domain
|
|
|
|
- bspinclude
|
|
|
|
- include
|
|
|
|
- *xparameters.h*
|
|
|
|
|
|
|
|
|
|
|
|
## Processing System GPIO Examples
|
|
|
|
|
|
|
|
**NOTE:** before starting, be sure that your **Ultra96-V2** is connected to the host and the **JTAG boot mode** is set:
|
|
|
|
- **Switch 1**: ON
|
|
|
|
- **Switch 2**: ON
|
|
|
|
|
|
|
|
![ultra96v2_boot_mode_jtag](uploads/6e9d1ce80866de489e609e4e70052b52/ultra96v2_boot_mode_jtag.jpg)
|
|
|
|
|
|
|
|
As a very simple but visual example, we will focus in the driver for the GPIO in the Processing System. In our standalone design for Ultra96-V2 in Vivado, we left the **GPIO in the Low Power Domain of the PS** marked as active.
|
|
|
|
|
|
|
|
In the Ultra96-V2, these are the PS GPIO that are connected to the board user LEDS and Pushbuttons and its associated pins:
|
|
|
|
|
|
|
|
| GPIO PS | PIN |
|
|
|
|
| :--- | ---: |
|
|
|
|
| USER_PUSH_BUTTON | 23 |
|
|
|
|
| USER_LED_O | 17 |
|
|
|
|
| USER_LED_1 | 18 |
|
|
|
|
| USER_LED_2 | 19 |
|
|
|
|
| USER_LED_3 | 20 |
|
|
|
|
|
|
|
|
If we review the list of available Peripheral Drivers, we can identify the following entry:
|
|
|
|
- **Peripheral**: *psu_gpio_0*
|
|
|
|
- **Driver**: *gpiops*
|
|
|
|
|
|
|
|
By clicking in the **Documentation** entry, we will have access to the Driver API in browsable HTML format.
|
|
|
|
|
|
|
|
Now, if we want to use the examples provided by Xilinx, we need to click in **Import Examples**:
|
|
|
|
|
|
|
|
![vitis_standalone_gpiops_import_examples](uploads/47bbceaecf85236c6cbe0df36ea70029/vitis_standalone_gpiops_import_examples.png)
|
|
|
|
|
|
|
|
After we do this, a dialog with the available examples for GPIO PS will appear:
|
|
|
|
|
|
|
|
![vitis_examples_for_gpiops](uploads/eaeceb1635411de4cd49a0a98db60e17/vitis_examples_for_gpiops.png)
|
|
|
|
|
|
|
|
### GPIO Polled Example
|
|
|
|
|
|
|
|
As a first example on how to use the GPIO PS driver, we will use the polled GPIO.
|
|
|
|
|
|
|
|
In order to do this, make the following selection to load **xgpiops_polled_example**:
|
|
|
|
|
|
|
|
![vitis_import_xgpiops_polled_example](uploads/610fc0b26ec860249964ae7ea5365ba0/vitis_import_xgpiops_polled_example.png)
|
|
|
|
|
|
|
|
After successfully importation, we will see that the following items have been created associated to *ultra96v2_standalone_platform*:
|
|
|
|
- **Application**: *xgpiops_polled_example_1*, the actual application
|
|
|
|
- **System**: *system_bsp_example_1*, the system containing the application
|
|
|
|
|
|
|
|
In addition, the file containing the source code, *xgpiops_polled_example.c* has been automatically opened in Vitis.
|
|
|
|
|
|
|
|
![vitis_standalone_xgpiops_polled_example_c](uploads/5048a7c7eddd8ad2723a8dacadec218b/vitis_standalone_xgpiops_polled_example_c.png)
|
|
|
|
|
|
|
|
The file contains comments about every software library and function it is being employed. One of the most important parts is the **include section**, when we should highlight the following entries:
|
|
|
|
- *xparameters.h*: where the application takes the actual peripheral definitions from.
|
|
|
|
- *xgpiops.h*: where the API for the Processing System GPIO is accessed.
|
|
|
|
- *xplatform_info.h*: where the information about our actual architecture is defined, e.g. MPSoC vs Versal vs Zynq-7000
|
|
|
|
|
|
|
|
Then, the application performs this test:
|
|
|
|
- Configure a LED as output and makes it blink 16 times
|
|
|
|
- Configure a Push Button as input and print the value in the serial port.
|
|
|
|
|
|
|
|
Before building, we must **modify the code** in the *xgpiops_polled_example.c* file to adapt to our setup.
|
|
|
|
|
|
|
|
First, as we are using a Cortex-A53 core, we must **slow down the blinking frequency** for the led to have a little more time for us when executing the example. In order to do this, in the **line 98** we can change from:
|
|
|
|
```c
|
|
|
|
#define LED_DELAY 10000000
|
|
|
|
```
|
|
|
|
To:
|
|
|
|
```c
|
|
|
|
#define LED_DELAY 50000000
|
|
|
|
```
|
|
|
|
|
|
|
|
Because the example was **targeted to the ZCU102** board when used on a MPSoC architecture, we need to **change the pin numbers** in the *xgpiops_polled_example.c* code to match those in the **Ultra96-V2**. In order to do this, in the **line 190**, we need to change from the *ZCU102* values:
|
|
|
|
```c
|
|
|
|
case XPLAT_ZYNQ_ULTRA_MP:
|
|
|
|
Input_Pin = 22;
|
|
|
|
Output_Pin = 23;
|
|
|
|
break;
|
|
|
|
```
|
|
|
|
|
|
|
|
to the *Ultra96-V2* ones, e.g.:
|
|
|
|
```c
|
|
|
|
case XPLAT_ZYNQ_ULTRA_MP:
|
|
|
|
Input_Pin = 23;
|
|
|
|
Output_Pin = 17;
|
|
|
|
break;
|
|
|
|
```
|
|
|
|
|
|
|
|
Once done, we **save the modifications** by clicking in the *Save* button of Vitis or using the *Ctrl+S* keyboard shortcut.
|
|
|
|
|
|
|
|
Now, we can use the *Assistant* view to build the application in *Debug* mode:
|
|
|
|
|
|
|
|
![vitis_xgpiops_polled_build](uploads/df9e635e448d4bd6ec2b4ee42a531410/vitis_xgpiops_polled_build.png)
|
|
|
|
|
|
|
|
After the build has been successful, we open the **Debug Configurations** from the *Assistant*:
|
|
|
|
|
|
|
|
![vitis_xgpiops_polled_debug_configurations](uploads/18e4547eda0a655826f2f8b075bb281e/vitis_xgpiops_polled_debug_configurations.png)
|
|
|
|
|
|
|
|
In the **Debug Configurations**, perform the following actions:
|
|
|
|
- Double click in the **Single Application Debug** entry in the left panel to create a new configuration.
|
|
|
|
- Change the **Name** field to *Debugger_xgpiops_polled_example_1* to identify our new configuration.
|
|
|
|
- In the **Project** field, click *Browse* and select *xgpiops_polled_example_1* from the available projects list.
|
|
|
|
|
|
|
|
Once done, **apply the changes** by clicking on the *Apply* button.
|
|
|
|
|
|
|
|
![vitis_xgpiops_polled_debug_main](uploads/1f14480956ccb2fa5fafb117970615c6/vitis_xgpiops_polled_debug_main.png)
|
|
|
|
|
|
|
|
Before proceeding, check the following tabs in your configuration:
|
|
|
|
- **Aplication**:
|
|
|
|
- *Debug/xgpiops_polled_example_1.elf* will be loaded to *psu_cortexa53_0* CPU.
|
|
|
|
- *Stop at 'Main'* option is checked.
|
|
|
|
- **Target Setup**:
|
|
|
|
- *Use FSBL flow for initialization* option is checked.
|
|
|
|
|
|
|
|
Once done, **power up the Ultra96-V2** and **launch the debug configuration** by clicking in *Debug*.
|
|
|
|
|
|
|
|
By doing this, you'll be switched to the **Debug perspective** and the program execution will be stopped at the *Main* method.
|
|
|
|
|
|
|
|
Make sure that you have the **Vitis Serial Terminal** program (or an external alternative) connected to **/dev/ttyUSB1**.
|
|
|
|
|
|
|
|
Now, **resume the execution** by clicking on *Resume* or by using the *F8* keyboard shortcut:
|
|
|
|
|
|
|
|
![vitis_debug_resume](uploads/e2976ea64701d5f945ccaff1f6e3d562/vitis_debug_resume.png)
|
|
|
|
|
|
|
|
If everything is OK, we will see one of the user LEDs in the Ultra96-V2 blinking (placed in the PCB border just between the USB sockets).
|
|
|
|
|
|
|
|
In addition, in the **serial terminal**, we will see the following messages:
|
|
|
|
|
|
|
|
![vitis_serial_terminal_xgpiops_polled](uploads/883fc6e16da3f393f18eddb0a69246dc/vitis_serial_terminal_xgpiops_polled.png)
|
|
|
|
|
|
|
|
Note that the **user push button in the Ultra96-V2 is active low**. If we relaunch the Debug process and press the push button when the LED is blinking, the GPIO input will be *0x0* (the user push button is the one placed close to the microSD socket).
|
|
|
|
|
|
|
|
Once we have finished with the debug session, we can **disconnect the debugger**:
|
|
|
|
|
|
|
|
![vitis_debug_disconnect](uploads/72627b7c9f7b54b8334502c86aa495b0/vitis_debug_disconnect.png)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### GPIO Interrupt Example
|
|
|
|
|
|
|
|
|
|
|
|
As a more advanced example on how to use the GPIO PS driver, we will use the interrupt driven GPIO.
|
|
|
|
|
|
|
|
In order to do this, make the following selection to load **xgpiops_intr_example**:
|
|
|
|
|
|
|
|
![vitis_import_xgpiops_intr_example](uploads/f3ec384a8ed475a4df33941f28e59e87/vitis_import_xgpiops_intr_example.png)
|
|
|
|
|
|
|
|
After successfully importation, we will see that the following items have been created associated to *ultra96v2_standalone_platform*:
|
|
|
|
- **Application**: *xgpiops_intr_example_1*, the actual application
|
|
|
|
- **System**: *system_bsp_example_2*, the system containing the application
|
|
|
|
|
|
|
|
In addition, the file containing the source code, *xgpiops_intr_example.c* has been automatically opened in Vitis.
|
|
|
|
|
|
|
|
![vitis_standalone_xgpiops_intr_example_c](uploads/e24c850349161bd704074d120d39b981/vitis_standalone_xgpiops_intr_example_c.png)
|
|
|
|
|
|
|
|
The file contains comments about every software library and function it is being employed. One of the most important parts is the **include section**, when we should highlight the following entries:
|
|
|
|
- *xparameters.h*: where the application takes the actual peripheral definitions from.
|
|
|
|
- *xgpiops.h*: where the API for the Processing System GPIO is accessed.
|
|
|
|
- *xscugic.h*: the generic interrupt controller driver component.
|
|
|
|
- *xil_exception.h*: contains exception related driver functions or macros to access the device.
|
|
|
|
- *xplatform_info.h*: where the information about our actual architecture is defined, e.g. MPSoC vs Versal vs Zynq-7000
|
|
|
|
|
|
|
|
Then, the application performs this test:
|
|
|
|
- Configure a LED as output and turn it off.
|
|
|
|
- Configure a Push Button as input.
|
|
|
|
- Configure the interrupt controller.
|
|
|
|
- Enable falling edge interrupts in the Push Button.
|
|
|
|
- Set an interrupt handling routine that turns on the LED and exit the program.
|
|
|
|
|
|
|
|
Before building, we must **modify the code** in the *xgpiops_intr_example.c* file to adapt to our Ultra96-V2 board.
|
|
|
|
|
|
|
|
Because the example was **targeted to the ZCU102** board when used on a MPSoC architecture, we need to **change the pin numbers** in the *xgpiops_intr_example.c* code to match those in the **Ultra96-V2**. In order to do this, in the **line 194**, we need to change from the *ZCU102* values:
|
|
|
|
```c
|
|
|
|
case XPLAT_ZYNQ_ULTRA_MP:
|
|
|
|
Input_Pin = 22;
|
|
|
|
Output_Pin = 23;
|
|
|
|
break;
|
|
|
|
```
|
|
|
|
|
|
|
|
to the *Ultra96-V2* ones, e.g.:
|
|
|
|
```c
|
|
|
|
case XPLAT_ZYNQ_ULTRA_MP:
|
|
|
|
Input_Pin = 23;
|
|
|
|
Output_Pin = 17;
|
|
|
|
break;
|
|
|
|
```
|
|
|
|
|
|
|
|
Now, if we take a look to the *XGpioPS_SetIntrType()* documentation, we have that:
|
|
|
|
|
|
|
|
```c
|
|
|
|
void XGpioPs_SetIntrType (
|
|
|
|
XGpioPs *InstancePtr,
|
|
|
|
u8 Bank,
|
|
|
|
u32 IntrType,
|
|
|
|
u32 IntrPolarity,
|
|
|
|
u32 IntrOnAny)
|
|
|
|
```
|
|
|
|
And the input arguments mean:
|
|
|
|
- *InstancePtr*: is a pointer to an XGpioPs instance.
|
|
|
|
- *Bank*: is the bank number of the GPIO to operate on. Valid values are 0 to XGPIOPS_MAX_BANKS - 1.
|
|
|
|
- *IntrType*: is the 32 bit mask of the interrupt type. 0 means Level Sensitive and 1 means Edge Sensitive.
|
|
|
|
- *IntrPolarity*: is the 32 bit mask of the interrupt polarity. 0 means Active Low or Falling Edge and 1 means Active High or Rising Edge.
|
|
|
|
- *IntrOnAny*: is the 32 bit mask of the interrupt trigger for edge triggered interrupts. 0 means trigger on single edge using the configured interrupt polarity and 1 means trigger on both edges.
|
|
|
|
|
|
|
|
In the **line 348**, we have the interrupt type configuration:
|
|
|
|
```c
|
|
|
|
/* Enable falling edge interrupts for all the pins in bank 0. */
|
|
|
|
XGpioPs_SetIntrType(Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);
|
|
|
|
```
|
|
|
|
So this is actually enabling *Level Sensitive* and *Active High*. As **the push-button in the Ultra96-V2 is Active Low when pressed**, this configuration would trigger the interrupt as soon as the program is executed.
|
|
|
|
|
|
|
|
In order to properly configure *Falling Edge* interrupts, we need to **modify the code** to:
|
|
|
|
```c
|
|
|
|
/* Enable falling edge interrupts for all the pins in bank 0. */
|
|
|
|
XGpioPs_SetIntrType(Gpio, GPIO_BANK, 0xFFFFFFFF, 0x0, 0x0);
|
|
|
|
```
|
|
|
|
|
|
|
|
Finally, in the **interrupt handling routine**, the program checks the value of the input and exits the program if the input pin value is not 0. As we are enabling falling edge interrupts, the input pin value will always be 0 when the interrupt is attended. In order to fix this, in **line 275** we must modify the if condition and fix the value for the output LED, so we change from:
|
|
|
|
```c
|
|
|
|
if (DataRead != 0) {
|
|
|
|
XGpioPs_SetDirectionPin(Gpio, Output_Pin, 1);
|
|
|
|
XGpioPs_SetOutputEnablePin(Gpio, Output_Pin, 1);
|
|
|
|
XGpioPs_WritePin(Gpio, Output_Pin, DataRead);
|
|
|
|
AllButtonsPressed = TRUE;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
to:
|
|
|
|
```c
|
|
|
|
if (DataRead == 0) {
|
|
|
|
XGpioPs_SetDirectionPin(Gpio, Output_Pin, 1);
|
|
|
|
XGpioPs_SetOutputEnablePin(Gpio, Output_Pin, 1);
|
|
|
|
XGpioPs_WritePin(Gpio, Output_Pin, 1);
|
|
|
|
AllButtonsPressed = TRUE;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Once done, we **save the modifications** by clicking in the *Save* button of Vitis or using the *Ctrl+S* keyboard shortcut.
|
|
|
|
|
|
|
|
Now, we can use the *Assistant* view to build the application in *Debug* mode:
|
|
|
|
|
|
|
|
![vitis_xgpiops_intr_build](uploads/1c76ddc3538a275e55c9f1d28b4ec395/vitis_xgpiops_intr_build.png)
|
|
|
|
|
|
|
|
After the build has been successful, we open the **Debug Configurations** from the *Assistant*:
|
|
|
|
|
|
|
|
![vitis_xgpiops_intr_debug_configurations](uploads/566a22100a0c482abf4d4ee430fd2dbc/vitis_xgpiops_intr_debug_configurations.png)
|
|
|
|
|
|
|
|
In the **Debug Configurations**, perform the following actions:
|
|
|
|
- Double click in the **Single Application Debug** entry in the left panel to create a new configuration.
|
|
|
|
- Change the **Name** field to *Debugger_xgpiops_intr_example_1* to identify our new configuration.
|
|
|
|
- In the **Project** field, click *Browse* and select *xgpiops_intr_example_1* from the available projects list.
|
|
|
|
|
|
|
|
Once done, **apply the changes** by clicking on the *Apply* button.
|
|
|
|
|
|
|
|
![vitis_xgpiops_intr_debug_main](uploads/1bb9410428309469eb4be679299308e3/vitis_xgpiops_intr_debug_main.png)
|
|
|
|
|
|
|
|
Before proceeding, check the following tabs in your configuration:
|
|
|
|
- **Aplication**:
|
|
|
|
- *Debug/xgpiops_intr_example_1.elf* will be loaded to *psu_cortexa53_0* CPU.
|
|
|
|
- *Stop at 'Main'* option is checked.
|
|
|
|
- **Target Setup**:
|
|
|
|
- *Use FSBL flow for initialization* option is checked.
|
|
|
|
|
|
|
|
Once done, **power up the Ultra96-V2** and **launch the debug configuration** by clicking in *Debug*.
|
|
|
|
|
|
|
|
By doing this, you'll be switched to the **Debug perspective** and the program execution will be stopped at the *Main* method.
|
|
|
|
|
|
|
|
Make sure that you have the **Vitis Serial Terminal** program (or an external alternative) connected to **/dev/ttyUSB1**.
|
|
|
|
|
|
|
|
Now, **resume the execution** by clicking on *Resume* or by using the *F8* keyboard shortcut:
|
|
|
|
|
|
|
|
![vitis_debug_resume](uploads/e2976ea64701d5f945ccaff1f6e3d562/vitis_debug_resume.png)
|
|
|
|
|
|
|
|
Just after we resume the execution, we will see that the **user LED is cleared** and, in the serial terminal, we will get the following message:
|
|
|
|
|
|
|
|
![vitis_serial_terminal_xgpiops_intr_ready](uploads/c19b61311ff525c0c131b9324af289d1/vitis_serial_terminal_xgpiops_intr_ready.png)
|
|
|
|
|
|
|
|
Then, as soon as we **push the button user button**, the *user LED is turned on* again and, in the serial terminal, we will be informed that the test was successful:
|
|
|
|
|
|
|
|
![vitis_serial_terminal_xgpiops_intr_success](uploads/336c33a5dd20c7be6115d6ca967c06ce/vitis_serial_terminal_xgpiops_intr_success.png)
|
|
|
|
|
|
|
|
Once we have finished with the debug session, we can **disconnect the debugger**:
|
|
|
|
|
|
|
|
![vitis_debug_disconnect](uploads/72627b7c9f7b54b8334502c86aa495b0/vitis_debug_disconnect.png) |