********
Overview
********

Introduction
============

The XMOS |I2S| library provides software defined, industry-standard,
|I2S| (Integrated Interchip Sound) components that allows you to
stream audio between devices using xCORE GPIO ports.

|I2S| is a specific type of PCM digital audio communication using
a serial clock (sometimes refered as bit clock) line,
word clock line and at least one multiplexed data line.

The library includes features such as |I2S| master (newly termed controller),
|I2S| slave (newly termed target), and TDM master components. 
This application note uses the library to create an |I2S| master digital loopback.

Block diagram
=============

.. figure:: ../images/block_diagram.drawio.png
   :width: 60%

   Application block diagram

The main application fits within one thread with a remote |I2C| task to
configure the audio hardware remotely from the other tile.
The ``lib_board_support`` library, which includes |I2C|, takes care of the
audio hardware setup.

The |I2S| task calls back to the ``i2s_loopback`` task, and the processing in
the ``i2s_loopback`` task occurs in-between the I/O operations of |I2S|.

|newpage|

*******************
|I2S| loopback demo
*******************

The CMakeLists.txt file
=======================

`XMOS` applications use the `xcommon-cmake <https://www.xmos.com/file/xcommon-cmake-documentation/?version=latest>`_
build and dependency management system. This is bundled with the `XMOS` XTC tools.

To start using the |I2S|, include ``lib_i2s`` as a dependent module in the
application's CMakeLists.txt file:

.. code-block:: cmake
   :caption: Identifying |I2S| as a dependent module

   set(APP_DEPENDENT_MODULES   "lib_i2s")

This demo also uses the |I2C| library (``lib_i2c``) which ``lib_board_support``
includes as a dependent module.
The application uses |i2C| to configure the audio CODECs.
Consequently, the application's CMakeLists.txt includes both ``lib_i2s`` and
``lib_board_support`` as dependent modules.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/CMakeLists.txt
   :caption: ``APP_DEPENDENT_MODULES`` definition
   :start-after: # Start of APP_DEPENDENT_MODULES
   :end-before: # End of APP_DEPENDENT_MODULES
   :dedent:
   :language: cmake

Includes
========

All xC files which declare the application ``main()`` function need to include
``platform.h``.
XMOS xcore specific defines for declaring and initialising hardware appear in
``xs1.h``.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Critical headers to include
   :start-at: include <platform.h>
   :end-at: include <xs1.h>
   :dedent:
   :language: c

The ``i2s.h`` file defines the |I2S| library functions.
This header must be included to use the library.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: The |I2S| header
   :start-at: include "i2s.h"
   :end-at: include "i2s.h"
   :dedent:
   :language: c

Another include gives access to the the board setup code.
It varies depending on the board in use.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Setup header for the XK-AUDIO-316-MC board
   :start-at: include "xk_audio_316_mc_ab/board.h"
   :end-at: include "xk_audio_316_mc_ab/board.h"
   :dedent:
   :language: c

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: Setup header for the XK-EVK-XU316 board
   :start-at: include "xk_evk_xu316/board.h"
   :end-at: include "xk_evk_xu316/board.h"
   :dedent:
   :language: c

Allocating hardware resources
=============================

An |I2S| interface requires both clock and data pins in order to
communicate with the audio CODEC device.
On an xcore the pins are controlled by ``ports``.

The ports used by the |I2S| library are declared on the tile on which they
reside.
Their declaration includes each port's direction and buffered nature.
Three 1-bit ports are used for the |I2S| clock signals.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Defining |I2S| clock ports
   :start-at: on tile[1]: in port p_mclk
   :end-at: on tile[1]: out port p_bclk
   :dedent:
   :language: c

This loopback application uses 1-bit ports for input and output.
The number of input and output ports, i.e., the value of ``NUM_I2S_LINES`` depends on the board in use.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Port definition for the XK-AUDIO-316-MC board
   :start-at: on tile[1]: buffered out port:32 p_dac[NUM_I2S_LINES]
   :end-at: on tile[1]: buffered in port:32 p_adc[NUM_I2S_LINES]
   :dedent:
   :language: c

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: Port definition for the XK-EVK-XU316 board
   :start-at: on tile[1]: buffered out port:32 p_dac[NUM_I2S_LINES]
   :end-at: on tile[1]: buffered in port:32 p_adc[NUM_I2S_LINES]
   :dedent:
   :language: c

The xcore also provides ``clock block`` hardware to efficiently generate
a clock signal that can either be driven out of a port or used
to control a port.
This application uses one clock block.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Clock block definition
   :start-at: on tile[1]: clock bclk
   :end-at: on tile[1]: clock bclk
   :dedent:
   :language: c

|newpage|

The application ``main()`` function
===================================

The ``main()`` function in the program sets up the tasks in the application.

Firstly, it declares ``interfaces`` and ``channels``.
XC interface and channel provides a means for concurrent tasks to communicate with each
other.
This application includes an interface for the |I2S| master and an interface/channel
for the |I2C| master.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: |I2S| Interface definition
   :start-at: interface i2s_frame_callback_if i_i2s;
   :end-at: interface i2s_frame_callback_if i_i2s;
   :dedent:
   :language: c

An interface or channel is used in different board for the |I2C| master.

When running on an ``XK-AUDIO-316-MC`` board, it includes an interface for the |I2C| master also.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: |I2C| Interface definition for the XK-AUDIO-316-MC board
   :start-at: interface i2c_master_if i_i2c[1]
   :end-at: interface i2c_master_if i_i2c[1]
   :dedent:
   :language: c

On the ``XK-EVK-XU316`` board, the application uses a channel instead.

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: |I2C| Channel definition for the XK-EVK-XU316 board
   :start-at: chan c_init
   :end-at: chan c_init
   :dedent:
   :language: c

The rest of the ``main()`` function starts all the tasks in parallel using the
xC ``par`` construct:

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Tasks for the XK-AUDIO-316-MC board
   :start-at: par {
   :end-before: return 0
   :dedent:
   :language: c

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: Tasks for the XK-EVK-XU316 board
   :start-at: par {
   :end-before: return 0
   :dedent:
   :language: c

This code starts the |I2S| master, the |I2C| master, the board setup logic,
and the loopback application.

The call to the ``i2s_loopback`` task in the ``par`` is marked with the
``[[distribute]]`` attribute, and the corresponding ``i2s_loopback()`` function
is marked with the ``[[distributable]]`` attribute.
These attributes mean that the ``i2s_loopback`` task will run on an existing
logical core if possible rather than creating a new one.
In this case it will share the logical core used by the |I2S| master.

Configuring audio CODECs
========================

All of the audio hardware is setup using functions in ``lib_board_support``. 
The previous inclusion of ``board.h`` from the ``xk_audio_316_mc_ab``
directory targets the hardware setup to the XU316 Multichannel Audio board(``XK-AUDIO-316-MC``)
and the ``xk_audio_316_mc_ab`` directory targets the XCORE.AI Evaluation Kit(``XK-EVK-XU316``).
These lines perform some board-specific initialisation and start the |I2C| task.

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: Board initialisation for the XK-AUDIO-316-MC board
   :start-at: xk_audio_316_mc_ab_board_setup(hw_config); // Setup must be done on tile[0]
   :end-before: xk_audio_316_mc_ab_i2c_master(i_i2c);      // Run I2C master server task to allow control from tile[1]
   :dedent:
   :language: c

.. note:: The XK-EVK-XU316 board does not need board-specific initialisation.

The ``hw_config`` struct specifies the hardware configuration.
In this case, it sets up the xcore to be an |I2S| master with the following
settings:

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: I2S master settings for the XK-AUDIO-316-MC board
   :start-at: #define SAMPLE_FREQUENCY
   :end-at: #define NUM_I2S_LINES 
   :dedent:
   :language: c

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: I2S master settings for the XK-EVK-XU316 board
   :start-at: #define SAMPLE_FREQUENCY
   :end-at: #define NUM_I2S_LINES 
   :dedent:
   :language: c

.. note:: ``lib_board_support`` only support changing master clock frequency in XK-EVK-XU316 Board

The following functions, called from the ``i2s_loopback`` task, complete the
initialisation and configuration of the ADCs and DACs:

.. code-block:: c
   :caption: ADCs and DACs initialisation and configuration for the XK-AUDIO-316-MC board

   xk_audio_316_mc_ab_AudioHwInit(i_i2c, hw_config);
   xk_audio_316_mc_ab_AudioHwConfig(i_i2c, hw_config, SAMPLE_FREQUENCY, MASTER_CLOCK_FREQUENCY, 0, DATA_BITS, DATA_BITS);


.. code-block:: c
   :caption: ADCs and DACs initialisation and configuration for the XK-EVK-XU316 board

   xk_evk_xu316_AudioHwInit(hw_config);
   xk_evk_xu316_AudioHwConfig(SAMPLE_FREQUENCY, MASTER_CLOCK_FREQUENCY, 0, DATA_BITS, DATA_BITS);

For full documentation of the ``lib_board_support`` API, please refer to the following link:
`lib_board_support <https://www.xmos.com/file/lib_board_support/>`_.

|newpage|

The ``i2s_loopback`` application
================================

The |I2S| loopback task provides the function of a digital loopback
so that all |I2S| samples received by the device will be forwarded on.

The task itself is declared as a ``[[distributable]]`` function
allowing it to share a logical core with other tasks.
This xC feature can be enabled for any task with the form:

.. code-block:: c
   :caption: Distributable task pattern

   // Task initialization code here
   while(1) {
     select {
       // Event cases here
     }
   }

The function takes a number of arguments:

.. literalinclude:: ../../app_an00162/app_an00162_xk_audio_316_mc/src/main.xc
   :caption: i2s_loopback function for the XK-AUDIO-316-MC board
   :start-at: [[distributable]]
   :end-at: void i2s_loopback(server i2s_frame_callback_if i_i2s, client i2c_master_if i_i2c)
   :dedent:
   :language: c

.. literalinclude:: ../../app_an00162/app_an00162_xk_evk_xu316/src/main.xc
   :caption: i2s_loopback function for the XK-EVK-XU316 board
   :start-at: [[distributable]]
   :end-at: void i2s_loopback(server i2s_frame_callback_if i_i2s)
   :dedent:
   :language: c

The interface to the |I2S| master, ``server i2s_frame_callback_if i_i2s``,
provides a set of callback functions.
The |I2S| master will call these functions as needed.

The ``i2s_loopback`` task uses the |I2C| master interface,
``client i2c_master_if i_i2c``, to configure the CODECs (ADCs and DACs)
remotely.

The main loop in the ``i2s_loopback`` task handles the |I2S| interface calls.

.. code-block:: c
   :caption: |I2S| task main loop
   
   while (1) {
   select {
      case i_i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
         i2s_config.mode = I2S_MODE_I2S;
         i2s_config.mclk_bclk_ratio = (MASTER_CLOCK_FREQUENCY / (SAMPLE_FREQUENCY * CHANS_PER_FRAME * DATA_BITS));
         ... // Audio HW Config for the board
         break;

      case i_i2s.receive(size_t n_chans, int32_t in_samps[n_chans]):
         for (int i = 0; i < n_chans; i++){
               samples[i] = in_samps[i]; // copy samples
         }
         break;

      case i_i2s.send(size_t n_chans, int32_t out_samps[n_chans]):
         for (int i = 0; i < n_chans; i++){
               out_samps[i] = samples[i]; // copy samples
         }
         break;

      case i_i2s.restart_check() -> i2s_restart_t restart:
         restart = I2S_NO_RESTART; // Keep on looping
         break;
      } // End select
   } // End while (1)

The |I2S| master library calls the ``init()`` method before it starts
any data streaming.
This call allows the application to reset and configure the audio CODECs,
for example when the sample rate changes.

The ``receive()`` interface method is called when the master has
received a frame of audio samples (all channels in one sample period).
The ``receive()`` method stores the samples in the ``samples`` array.

The |I2S| master calls the ``send()`` interface method when it needs a new
frame of samples to send.
In this case the application simply returns the frame of samples previously
received.

Finally, the ``restart_check()`` method is called by the |I2S| master
once per frame.
It allows the application to control restart or shutdown of the |I2S| master.
In this case the application continues to run forever and so always returns
``I2S_NO_RESTART``.

|newpage|


************************
Building the application
************************

The following section assumes you have downloaded and installed the
`XMOS XTC tools <https://www.xmos.com/software-tools/>`_.
See the `README` file for required version.
Installation instructions can be found `here <https://xmos.com/xtc-install-guide>`_.
Be sure to pay attention to the section `Installation of required third-party tools
<https://www.xmos.com/documentation/XM-014363-PC-10/html/installation/install-configure/install-tools/install_prerequisites.html>`_.

The application uses the `xcommon-cmake <https://www.xmos.com/file/xcommon-cmake-documentation/?version=latest>`_
build system as bundled with the XTC tools.

The ``AN00162_i2s_loopback_demo`` software zip-file should be downloaded and
unzipped to a chosen directory.

To configure the build, run the following from an XTC command prompt::

    cd an00162
    cd app_an00162
    cmake -G "Unix Makefiles" -B build

All required dependencies are included in the software download.
However, if any are missing, they will be downloaded by the build system.

Finally, the application binaries can be built using ``xmake``::

    xmake -j -C build

The application uses approximately 3 kiB on Tile[0] and 7 kiB on Tile[1]
(each tile has 512 kiB available).


*******************
Demo hardware setup
*******************

Please refer to the `XU316 Multichannel Audio board <https://www.xmos.com/download/XCORE_AI-Multichannel-Audio-Platform-1V1-Hardware-Manual(1V1).pdf>`_ 
and `XCORE.AI Evaluation Kit <https://www.xmos.com/download/xcore_ai-Evaluation-Kit-v2_0-hardware-manual(2V0).pdf>`_ 
hardware platform documentation.

The demo is designed to run on the XU316 Multichannel Audio board and XCORE.AI Evaluation Kit.
To run the demo:

 * Connect a USB cable from your host to the DEBUG connector.
 * Connect a USB cable from your host to the USB DEVICE connector.
 * Connect a sound source to the 3.5mm line in.
   Channels 1-2, 3-4, 5-6 or 7-8 can be used.
 * Connect headphones or speakers to the corresponding line out.


****************************
Running the demo application
****************************

To run the application return to the ``app_an00162`` directory and run the
following command:

.. code-block:: bash
   :caption: For the XK-AUDIO-316-MC board
   
   xrun app_an00162_xk_audio_316_mc/bin/app_an00162_xk_audio_316_mc.xe


.. code-block:: bash
   :caption: For the XK-EVK-XU316 board
   
   xrun app_an00162_xk_evk_xu316/bin/app_an00162_xk_evk_xu316.xe

You should hear the audio connected to the analog input jacks looped back to
the output jacks.


**********
References
**********

 * XMOS Tools User Guide

   https://www.xmos.com/documentation/XM-014363-PC-9/html/

 * XMOS xcore Programming Guide

   https://www.xmos.com/published/xmos-programming-guide

 * XMOS Libraries

   https://www.xmos.com/libraries/

 * |I2S| Protocol

   https://en.wikipedia.org/wiki/I2S
