XK-EVK-XU316: accessing the GPIO

New to XMOS and XCore? Get started here.
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

XK-EVK-XU316: accessing the GPIO

Post by Harold Barrel »

Hello everyone!
I'm getting started with the XK-EVK-XU316 and I'm already stuck on the simplest task :-D
I started from the unmodified app_usb_aud_xk_evk_xu316, which I compiled, ran, and tested. Everything works fine!
XTC version is 15.2.1, sw_usb_audio version is 8.0.0,

What I need to do is setting some GPIO pins based on some input stream properties (PCM or DSD input and sampling rate).
For example, I'd like to turn on LED 2 when the sampling frequency is a multiple of 44.1kHz, and turn it off when it's a multiple of 48kHz.

After looking at how LEDs are turned on/off for the XK-316-MC board, and finding the correct LED port in the XK-EVK-XU316 xcore.ai Evaluation Kit Manual (it's port 4C), I did the following modifications to "src/extensions/audiohw.xc".

At the top, I added the port definition:

Code: Select all

on tile[0]: out port p_leds = XS1_PORT_4C;

Then, I added these the lines "p_leds <: 0x2;" and "p_leds <: 0x0;" inside "void AudioHwConfig" to turn on/off LED 2, respectively:

Code: Select all

void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
    unsigned sampRes_DAC, unsigned sampRes_ADC)
{
    assert(samFreq >= 22050);

    // Set the AppPLL up to output MCLK.
    if ((samFreq % 22050) == 0)
    {
        // Disable the PLL
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF));
        // Enable the PLL to invoke a reset on the appPLL.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);
        // Must write the CTL register twice so that the F and R divider values are captured using a running clock.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);
        // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF));
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441);

        // Set the fractional divider if used
        write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_44);

        // Turn on LED 2 (samFreq multiple of 44100)
        p_leds <: 0x2;
    }
    else if ((samFreq % 24000) == 0)
    {
        // Disable the PLL
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF));
        // Enable the PLL to invoke a reset on the appPLL.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);
        // Must write the CTL register twice so that the F and R divider values are captured using a running clock.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);
        // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values.
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF));
        write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48);

        // Set the fractional divider if used
        write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_48);

        // Turn off LED 2 (samFreq multiple of 48000)
        p_leds <: 0x0;
    }

    // Wait for PLL output frequency to stabilise due to fractional divider enable
    delay_microseconds(100);

    // Turn on the clock output
    write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, APP_PLL_DIV);
}

The code compiles without errors, but when I launch the executable with xrun, my computer isn't able to use the board as an audio device anymore.
These are the related kernel logs from my Linux machine:

Code: Select all

apr 23 12:33:56 michele-xps9700 kernel: usb 1-2.4: new full-speed USB device number 43 using xhci_hcd
apr 23 12:33:57 michele-xps9700 kernel: usb 1-2.4: new full-speed USB device number 44 using xhci_hcd
apr 23 12:33:57 michele-xps9700 kernel: usb 1-2.4: New USB device found, idVendor=20b1, idProduct=0019, bcdDevice= 7.31
apr 23 12:33:57 michele-xps9700 kernel: usb 1-2.4: New USB device strings: Mfr=1, Product=3, SerialNumber=2
apr 23 12:33:57 michele-xps9700 kernel: usb 1-2.4: Product: XMOS xCORE (UAC1.0)
apr 23 12:33:57 michele-xps9700 kernel: usb 1-2.4: Manufacturer: XMOS
apr 23 12:33:59 michele-xps9700 pipewire[1080]: spa.alsa: hw:sofsoundwire,2p: (375 suppressed) snd_pcm_avail after recover: Broken pipe
apr 23 12:34:02 michele-xps9700 pipewire[1080]: spa.alsa: hw:sofsoundwire,2p: (375 suppressed) snd_pcm_avail after recover: Broken pipe
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 1:1: cannot set freq 48000 to ep 0x1
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: cannot set freq 48000 to ep 0x82
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: failed to get current value for ch 1 (-22)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: failed to get current value for ch 2 (-22)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 11:0: failed to get current value for ch 1 (-22)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 11:0: failed to get current value for ch 2 (-22)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 11:0: cannot get min/max values for control 2 (id 11)
apr 23 12:34:02 michele-xps9700 mtp-probe[18275]: checking bus 1, device 44: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4"
apr 23 12:34:02 michele-xps9700 mtp-probe[18275]: bus: 1, device: 44 was not an MTP device
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 10:0: cannot get min/max values for control 2 (id 10)
apr 23 12:34:02 michele-xps9700 mtp-probe[18297]: checking bus 1, device 44: "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.4"
apr 23 12:34:02 michele-xps9700 mtp-probe[18297]: bus: 1, device: 44 was not an MTP device
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)
apr 23 12:34:02 michele-xps9700 kernel: usb 1-2.4: 2:1: usb_set_interface failed (-32)

EDIT: I'm also attaching the output of xgdb below.

Code: Select all

Program received signal ET_ILLEGAL_RESOURCE, Resource exception.
[Switching to tile[1] core[0]]
AudioHwConfig (samFreq=<value optimized out>, mClk=<value optimized out>, dsdMode=<value optimized out>, sampRes_DAC=<value optimized out>, sampRes_ADC=24)
    at ../src/extensions/audiohw.xc:316
316             p_leds <: 0x2;

Many thanks in advance for your help.
Michele


User avatar
CousinItt
Respected Member
Posts: 365
Joined: Wed May 31, 2017 6:55 pm

Post by CousinItt »

On which tile are you running AudioHwConfig()? The software only has direct access to ports on the same tile.
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

Post by Harold Barrel »

Hello, thanks for your reply!

I've been doing some reading in the meantime and I arrived to same conclusion... It really looks like AudioHwConfig() is running of tile 0, while the LED pins are on tile 1.

Can AudioHwConfig() be easily moved to tile 1?

Or should I use a so-called channel? I found about them in the developer documentation, but I don't really know how to adapt that example to my case.
User avatar
CousinItt
Respected Member
Posts: 365
Joined: Wed May 31, 2017 6:55 pm

Post by CousinItt »

I would expect that if AudioHwConfig() is on tile 1, it's there for a good reason. I don't know much about this code, but there appears to be AudioHwRemote() running on tile 0. Maybe you could use an additional channel for controlling the LEDs.
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

Post by Harold Barrel »

CousinItt wrote: Wed Apr 24, 2024 3:04 pm I would expect that if AudioHwConfig() is on tile 1, it's there for a good reason.
Right.
CousinItt wrote: Wed Apr 24, 2024 3:04 pm I don't know much about this code, but there appears to be AudioHwRemote() running on tile 0. Maybe you could use an additional channel for controlling the LEDs.
Yeah, I also noticed that.
I tried to learn by example from how the "uc_audiohw" channel is set up, but I failed miserably.
I really don't get it, everything looks very scattered to me and I can't really understand how to add another channel and use it between those functions.


I also thought: why not try using lib_gpio, since its README says that "it also allows accessing ports across separate XMOS".
Unfortunately this is what happens when I add lib_gpio to USED_MODULES:

Code: Select all

xmake all
Checking build modules
Using build modules: lib_xua(4.0.0) lib_i2c(6.2.0) lib_gpio(2.2.0) lib_adat(1.1.0) lib_locks(2.2.0) lib_logging(3.2.0) lib_mic_array(4.6.0) lib_spdif(6.1.0) lib_sw_pll(2.1.0) lib_xassert(4.2.0) lib_xud(2.3.1) lib_dsp(6.3.0)
Analyzing gpio.xc
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:4:10: fatal error: 'gpio.h' file not found
#include <gpio.h>
         ^
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:9:38: error: parse error before "i"
void input_gpio(server input_gpio_if i[n], static const size_t n,
                                     ^
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:10:47: error: parse error before ')' token
                in port p, char (&?pin_map)[n])
                                              ^
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:9:6: error: array type has incomplete element type
void input_gpio(server input_gpio_if i[n], static const size_t n,
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:10:45: error: size of array is not constant or based on a const static parameter
                in port p, char (&?pin_map)[n])
                                            ^
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:9:1: error: function cannot return an array type
void input_gpio(server input_gpio_if i[n], static const size_t n,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:9:1: error: combinable function must have void return type
void input_gpio(server input_gpio_if i[n], static const size_t n,
^~~~
/home/michele/Projects/xmos/lib_gpio/lib_gpio/src/gpio.xc:12:9: error: parse error before "pmap"
  char *pmap;
        ^
xmake[1]: *** [.build_2AMi2o2xxxxxx/_l_gpio/src/gpio.xc.pca.xml.decouple] Error 1
xmake: *** [analyze] Error 2

Things are quite desperate.
Last edited by Harold Barrel on Wed Apr 24, 2024 5:31 pm, edited 1 time in total.
User avatar
fabriceo
XCore Addict
Posts: 188
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hello
There is already a mechanism implemented to transfer I2C read/write over the 2 tiles and you just have to leverage it.
Just add new commands in the AudioHwRemote2() to modify your LEDs and create some functions using this commands like done in CODEC_REGWRITE and CODEC_REGREAD. No need for creating a new channel

hope this helps
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

Post by Harold Barrel »

fabriceo wrote: Thu Apr 25, 2024 7:57 am Just add new commands in the AudioHwRemote2() to modify your LEDs and create some functions using this commands like done in CODEC_REGWRITE and CODEC_REGREAD. No need for creating a new channel
Hello Fabriceo, I tried doing that yesterday but I got stuck at some point and threw the code away.
Let me try again today with a fresh head, I'll report here if I make progress.


In the meantime I also found a topic where a user tries to blink LEDs on the same board, however without any communication between tiles.
Anyway, it shows how to modify user_main.h and add an user_main.c, so that could be useful for my future endeavors as well.


Admins: is it really necessary that each of my posts has to be approved manually? :-/
Last edited by Harold Barrel on Thu Apr 25, 2024 8:08 am, edited 1 time in total.
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

Post by Harold Barrel »

Okay, it works!! See the diff below.
Now I'll have to add a read function as well, otherwise I can't toggle all 4 LEDs independently.
I'll keep you posted, thanks again for the advice.

Code: Select all

diff --git a/app_usb_aud_xk_evk_xu316/src/extensions/audiohw.xc b/app_usb_aud_xk_evk_xu316/src/extensions/audiohw.xc
index ae6eefd..8cd1756 100644
--- a/app_usb_aud_xk_evk_xu316/src/extensions/audiohw.xc
+++ b/app_usb_aud_xk_evk_xu316/src/extensions/audiohw.xc
@@ -5,6 +5,9 @@
 #include "i2c.h"
 #include "tlv320aic3204.h"
 
+// LEDs 1-4 line
+on tile[0]: out port p_leds = XS1_PORT_4C;
+
 // CODEC I2C lines
 on tile[0]: port p_i2c_scl = XS1_PORT_1N;
 on tile[0]: port p_i2c_sda = XS1_PORT_1O;
@@ -57,7 +60,8 @@ on tile[1]: out port p_codec_reset  = PORT_CODEC_RST_N;
 typedef enum
 {
     AUDIOHW_CMD_REGWR,
-    AUDIOHW_CMD_REGRD
+    AUDIOHW_CMD_REGRD,
+    AUDIOHW_CMD_LEDWR
 } audioHwCmd_t;
 
 static inline void AIC3204_REGREAD(unsigned reg, unsigned &val, client interface i2c_master_if i2c)
@@ -85,13 +89,19 @@ void AudioHwRemote2(chanend c, client interface i2c_master_if i2c)
             AIC3204_REGREAD(regAddr, regVal, i2c);
             c <: regVal;
         }
-        else
+        else if(cmd == AUDIOHW_CMD_REGWR)
         {
             unsigned regAddr, regValue;
             c :> regAddr;
             c :> regValue;
             AIC3204_REGWRITE(regAddr, regValue, i2c);
         }
+        else if(cmd == AUDIOHW_CMD_LEDWR)
+        {
+            unsigned ledValue;
+            c :> ledValue;
+            p_leds <: ledValue;
+        }
     }
 }
 
@@ -127,6 +137,15 @@ static inline void CODEC_REGREAD(unsigned reg, unsigned &val)
     }
 }
 
+static inline void LEDS_SET(unsigned val)
+{
+    unsafe
+    {
+        uc_audiohw <: (unsigned) AUDIOHW_CMD_LEDWR;
+        uc_audiohw <: val;
+    }
+}
+
 /* Note this is called from tile[1] but the I2C lines to the CODEC are on tile[0]
  * use a channel to communicate CODEC reg read/writes to a remote core */
 void AudioHwInit()
@@ -306,6 +325,9 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
 
         // Set the fractional divider if used
         write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_44);
+
+        // Turn on LED 2 (samFreq multiple of 44100)
+        LEDS_SET(0x2);
     }
     else if ((samFreq % 24000) == 0)
     {
@@ -321,6 +343,10 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode,
 
         // Set the fractional divider if used
         write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_48);
+
+        // Turn off LED 2 (samFreq multiple of 48000)
+        // (this actually turns off all LEDs)
+        LEDS_SET(0x0);
     }
 
     // Wait for PLL output frequency to stabilise due to fractional divider enable
User avatar
fabriceo
XCore Addict
Posts: 188
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Harold,
good!
you can also implement a 2 parameters function doing both a "and' and then a "or".
Thats my preferred option in another library which also gives an atomic approach regarding multiple clients.

void andor(int x, int y) { port = (port & x) | y; }

but for sure now your leds are already nicely blinking
User avatar
Harold Barrel
Junior Member
Posts: 7
Joined: Tue Apr 23, 2024 9:05 am

Post by Harold Barrel »

Hi Fabriceo,
fabriceo wrote: Fri Apr 26, 2024 6:53 am you can also implement a 2 parameters function doing both a "and' and then a "or".
yes that looks nice! Thanks you for the advice.
Hopefully this will help someone in the future.
Maybe this could be integrated into the XMOS example code? LED blinking is a pretty basic thing, I think it could fit there nicely :-)