A key feature of Avnet's Ultra96-V2 board is its WiFi and Bluetooth connectivity that is made possible using the on-board ATWILC3000 module from Microchip. This module is IEEE 802.11 b/g/n plus Bluetooth 5 LE and is certified in more than 75 countries globally. The module provides SDIO (WiFi) and UART (Bluetooth) interfaces for connecting to the host processor and can achieve WiFi throughput of up to 46 Mbps UDP & 28 Mbps TCP/IP. Though this is a very popular module that is used in countless products, we did have some challenges when integrating it for use in Linux on the Ultra96-V2 board. A few of the challenges we faced were:
- Arm v7 vs Arm v8 architecture and Linux kernel v4.9 vs. v4.14
- Device driver patch
- Device tree settings
- Ever-changing application source files
- Xilinx shell script and configuration file require patches
Arm v7 vs Arm v8 architecture and Linux kernel v4.9 vs v4.14
Perhaps the most significant challenge we faced in our development with the ATWILC3000 module was that the device driver was targeted to Arm v7 CPU architecture (e.g. Arm Cortex-A9, etc.), but the Xilinx Zynq UltraScale+ device on the Ultra96-V2 board has an Arm v8 CPU (quad core Cortex-A53). Though this module was known to work and had been tested with many Microchip Arm MCUs and CPUs, none of them were Arm v8 like the Zynq UltraScale+. The difference is in how memory gets allocated and shared up and down the driver stack. This issue was also exacerbated by changes in the Linux kernel. In Linux kernel v4.9 (PetaLinux 2017.x) the driver worked as expected and we used this kernel for our initial testing of this module with the Ultra96-V2 board. However, starting with Linux kernel v4.14 (PetaLinux 2018.x) it became clear that the driver was now broken. The Linux device driver was no longer able to read the chipid from the ATWILC3000 module, and this of course made the driver non-functional. The v4.9 Linux kernel allowed the driver to use a local stack variable to pass data up the stack and the v4.14 Linux kernel changed this to require that kmalloc be used instead. Once we changed the driver to use kmalloc we were able fetch the chipid from the WiFi module and the device driver started working again. This is all likely because the Arm v8 architecture introduced support to control access to shared memories, and this change was implemented in the Linux v4.14 kernel.
Device driver patch
In addition to the kmalloc issue with the device driver, we also needed to patch a Linux kernel source to correctly toggle the module's reset pin at boot time. This is a requirement for the ATWILC3000 module to be initialized in a known-good state. The device driver originally performed this function, but it did not work correctly so we disabled it in the device driver and instead moved this task to the drivers/mmc/core/pwrseq_simple.c kernel source file. This patch is supplied as part of a kernel configuration bitbake recipe in the PetaLinux BSP.
Device tree settings
In additional to having a working Linux device driver, it is also critical to make sure the device tree settings are correct. If the device tree description for the WiFi module is incorrect, Linux won't know how to make the connection between the hardware device and the device driver in the system. For the ATWILC3000 there are two parts to the device tree description - one for the toggling of the module's reset pin and another to describe the SDIO interface and "connect" it to the WiFi module.
Description of connections for toggling the reset pin:
/ {
sdio_pwrseq: sdio_pwrseq { /* This needs to be able to manipulate the chip_en and the resetn properly */
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio 7 1>; // MIO[7] for WILC3000 RESETN, Active low
chip_en-gpios = <&gpio 8 1>;
};
};
Description of the attachment of the device driver to the SDIO interface:
&sdhci1 {
bus-width= <0x4>;
max-frequency = <50000000>;
/delete-property/cap-power-off-card; // This is not compatible with the WILC3000 and means the WILC will always be powered on
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
wilc_sdio@1 {
compatible = "microchip,wilc3000", "microchip,wilc3000";
reg = <0>;
bus-width= <0x4>;
status = "okay";
};
};
Ever-changing application source files
One of the applications that is included in the PetaLinux BSP that we post for users to download is a Python webserver. This webserver is created and maintained by Xilinx. In addition to acting as the webserver for the example projects and documentation, etc. stored in the Linux filesystem on the Ultra96-V2 board, this application also takes care configuring the WiFi interface via the on-board access point. This configuration mechanism has undergone several changes in recent PetaLinux versions. Packaged along with this webserver are accompanying html pages, etc. for the webpages it serves. Because these files are different for the Ultra96-V1 vs the Ultra96-V2 board, Avnet maintains a separate "ultra96v2" branch in the git repository for these sources that are specific to the Ultra96-V2 board. This requires Avnet to keep this branch updated each time Xilinx makes a change to the master branch of Ultra96-V1 files. Keeping up with the changes that Xilinx makes to this webserver in the "master" branch and incorporating them into the "ultra96v2" branch can be a bit of a challenge and cumbersome process of git commits and pull requests.
Xilinx shell script and configuration file require patches
In the "out of box" Linux OS image for the Ultra96 there is a shell script (/usr/share/wpa_ap/ap.sh) that runs at Linux boot time that loads the ATWILC3000 device driver and starts the WiFi access point (AP). This AP allows any WiFi device to connect to the WiFi AP SSID (ULTRA96-V2_<MAC ID>) and open the webpage (http://192.168.2.1/wifi_setup.html) to configure the board to connect to a WLAN by providing the WLAN SSID and password. This script is created and maintained by Xilinx and was first used with the Ultra96-V1 board which had a Texas Instruments WiLink8 WiFi & Bluetooth module installed. The device driver for the WiLink8 provides two interfaces for WiFi connectivity - one for the AP (wlan1) and another for the WLAN connection (wlan0). This script was written with "wlan0" and "wlan1" hard coded to match the WiLink8 device driver and at that time it was never expected that the WiFi module would change. However, Avnet needed to spin a rev of the Ultra96 board - now named Ultra96-V2 - to replace an end-of-life DDR4 memory device and to change the WiFi & Bluetooth module to the Microchip ATWILC3000. The device driver for the ATWILC3000 provides the same two interfaces but they are named "p2p0" (AP) and "wlan0" (WLAN). This meant that this script had to be updated to match the ATWILC3000 device driver and the best way to do that was via a patch that gets applied via a bitbake recipe at PetaLinux build time. There is also a configuration file (/usr/share/wpa_ap/udhcpd.conf) that configures a DHCP server that is used by the AP to serve IP addresses to those WiFi devices that connect to the AP's SSID. Like the AP shell script this was also hard coded with "wlan1" to match the AP interface provided by the WiLink8 device driver. Avnet also provides a patch and bitbake recipe to change this to "p2p0" at PetaLinux build time.
As ubiquitous as WiFi connectivity has become in our lives - from smart phones and gadgets to cars and appliances - the engineer's task of integrating it into the hardware and software of these devices remains a sometimes arduous and complex task. For all of the development boards that Avnet has created over the years integrating WiFi and Bluetooth connectivity has always been a challenge - especially for the software. This blog post highlights some of those challenges and hopefully provides a blueprint to follow for your own development with theUltra96-V2 board. All of the patches and fixes mentioned here have been integrated into the PetaLinux BSPs that Avnet provides for the Ultra96-V2 board. It is recommended that any user looking to do any development with this board start with these BSPs.
Here are a few more useful links related to the Ultra96-V2 board and the Microchip ATWILC3000 module:
Microchip ATWILC3000 product page
Avnet's git repo for the modified ATWILC3000 Linux device driver sources
Microchip's official git repo for the ATWILC3000 Linux device driver sources
Xilinx git repo for the Python webserver
Top Comments