.. include:: ../../../README.rst

.. include:: resource_usage_summary.rst

|newpage|

External Signal Description
***************************

The library implements the S/PDIF (Sony/Philips Digital Interface
Format) protocol for transporting uncompressed stereo PCM data of up to 24bits.

.. note:: The S/PDIF signal shown in the diagrams below are digital representations of S/PDIF and not an actual signal
   suitable for external devices (which is 0.5V pk-pk etc). External circuitry is required to interface with the chosen
   medium (optical or electrical).

Connecting to the xCORE as Transmitter
======================================

The precise transmission frequencies supported depend on the availability
of an external clock (e.g. a PLL or a crystal oscillator) that runs at a
frequency of *channels* * *sampleRate* * *64* or a power-of-2
multiple. For example, for 2 channels at 192 KHz the
external clock has to run at a frequency of 24.576 MHz. This same frequency
also supports 2 channels at 48 KHz (which requires a minimum frequency of
6.144 MHz). If both 44,1 and 48 KHz frequencies are to be supported, both a
24.576 MHz and a 22.579 MHz master clock is required.

When using an xcore.ai based device these frequencies can be generated by the on-chip
application/secondary PLL.

The connection of an S/PDIF transmit line to the xCORE is shown in
:ref:`spdif_connect_tx`.

.. _spdif_connect_tx:

.. figure:: images/spdif_tx_connect.*
   :width: 60%

   Connecting S/PDIF transmit

The output signal will contain jitter at the level of +/-1 core clock (<2ns for a 500 MHz xcore)
this is typically inconsequential but if lower jitter levels are desired the signal can be re-clocked
by the external master clock to reduce the jitter to that of the external master clock.
A simple D-type flip flop can be used for this purpose.

The incoming clock signal is used to drive an internal clock and can be shared with other software
functions using the same master clock (e.g. ADAT transmit or I2S).

.. note:: The transmit stream user bits are set to 0. The validity bits are set to 0 (i.e. valid).

Connecting to the xCORE as Receiver
===================================

The receiver can receive stereo PCM signals up to 192 KHz.

The connection of an S/PDIF receiver line to the xCORE is shown in
:ref:`spdif_connect_rx`.

.. _spdif_connect_rx:

.. figure:: images/spdif_rx_connect.*
   :width: 45%

   Connecting S/PDIF receiver

.. note:: Only a single wire is connected - the clock is recovered from the incoming data stream.

Usage
*****

All S/PDIF functions can be accessed via the ``spdif.h`` header::

  #include <spdif.h>

``lib_spdif`` should also be added to the
``USED_MODULES`` field of the application Makefile.

S/PDIF Transmitter
==================

S/PDIF components are instantiated as parallel tasks that run in a
``par`` statement. The application can connect via a channel
connection.

.. _spdif_tx_task_diag:

.. figure:: images/spdif_tx_task_diag.*
   :width: 60%

   S/PDIF transmit task diagram

For example, the following code instantiates an S/PDIF transmitter component
and connects to it::

  buffered out port:32 p_spdif_tx = XS1_PORT_1K;
  in port p_mclk_in = XS1_PORT_1L;
  clock clk_audio = XS1_CLKBLK_1;

  int main(void)
  {
    chanend c_spdif;
    par
    {
      on tile[0]:
      {
         spdif_tx_port_config(p_spdif_tx, clk_audio, p_mclk_in, 0);
         spdif_tx(p_spdif_tx, c_spdif);
        }

      on tile[0]: my_application(c_spdif);
    }
    return 0;
  }

The helper function ``spdif_tx_port_config()`` clocks the clock-block from the master clock
port and, in turn, clocks the S/PDIF transmit port from this clock-block.

The application can communicate with the components via API functions
that take the channel end as arguments e.g.::

  void my_application(chanend c_spdif)
  {
    int32_t sample = 0;
    spdif_tx_reconfigure_sample_rate(c, 96000, 12288000);
    while (1)
    {
      sample++;
      spdif_tx_output(c_spdif, sample, sample + 1);
    }
  }

Configuring the Underlying Clock
--------------------------------

When using the transmit component, the internal clock needs to be
configured to run off the incoming signal e.g.::

    spdif_tx_port_config(p_spdif_tx, clk_audio, p_mclk_in, 7);

This function needs to be called before the ``spdif_tx()`` function in
the programs ``par`` statement.


In this function the ``configure_clock_src()`` is used configure a clock to run off an
incoming port - see the :ref:`XMOS Programming Guide<programming_guide>` for more information.

The last parameter is used with the ``set_clock_fall_delay()`` function to configure an
internal delay from the incoming clock signal to the internal clock's falling edge.
This is done to allow for the correct alignment of outgoing data with
the master clock at the external D-type flip-flop.

Note, the delay value shown above is a typical example and may need to be
tuned for the specific hardware being used.

S/PDIF Receiver
===============

S/PDIF components are instantiated as parallel tasks that run in a
``par`` statement. The application can connect via a channel
connection.

.. _spdif_rx_task_diag:

.. figure:: images/spdif_rx_task_diag.*
   :width: 60%

   S/PDIF receiver task diagram

For example, the following code instantiates an S/PDIF receiver component
and connects to it::

  port p_spdif_rx  = XS1_PORT_1F;
  clock audio_clk  = XS1_CLKBLK_1;

  int main(void)
  {
      streaming chan c;
      par
      {
        spdif_rx(c, p_spdif_rx, audio_clk, 96000);
        handle_samples(c);
      }
      return 0;
  }

The application can communicate with the components via API functions
that take the channel end as arguments e.g.::

 void my_application(streaming chanend c)
 {
  int32_t sample;
  size_t index;
  size_t left_count = 0;
  size_t right_count = 0;
  while(1)
  {
    select
    {
      case spdif_rx_sample(c, sample, index):
      // sample contains the 24bit data
      // You can process the audio data here
      if (index == 0)
        left_count++;
      else
        right_count++;
      break;
    }
    ...

Note that a program can react to incoming samples using a
``select`` statement. More information on using ``par`` and ``select``
statements can be found in the :ref:`XMOS Programming Guide<programming_guide>`.

Each 32-bit word received from the receive component via the channel has the following format:

+----------+----------------+
| Bit(s)   | Field          |
+==========+================+
| 3:0      | Preamble       |
+----------+----------------+
| 7:4      | Auxiliary data |
+----------+----------------+
| 27:8     | Audio sample   |
+----------+----------------+
| 28       | Validity       |
+----------+----------------+
| 29       | User           |
+----------+----------------+
| 30       | Control        |
+----------+----------------+
| 31       | Parity         |
+----------+----------------+

.. note:: The four auxilary data bits are typically used to extend the audio sample from 20 to 24 bits.

The ``spdif_rx_sample()`` helper function strips away all fields other than the Audio Sample and Auxiliary data and returns
this audio sample data in the upper 24 bits of the ``sample`` variable.

Should other fields be desired - for parity checking, for instance, regular channel communication syntax can be used. For example::

 void my_application(streaming chanend c)
 {
  int32_t sample;
  size_t count = 0;

  while(1)
  {
      c :> spdif_data;

      // Check parity
      int parity_error = spdif_rx_check_parity(spdif_data);

      if (parity_error == 0)
        count++;
    }
    ...


|newpage|

API
***

Creating an S/PDIF Receiver Instance
====================================

.. doxygenfunction:: spdif_rx

|newpage|

S/PDIF Receiver API
===================

.. doxygenfunction:: spdif_rx_sample
.. doxygenfunction:: spdif_rx_shutdown
.. doxygenfunction:: spdif_rx_check_parity

|newpage|

Creating an S/PDIF Transmitter Instance
=======================================

.. doxygenfunction:: spdif_tx_port_config
.. doxygenfunction:: spdif_tx

|newpage|

S/PDIF Transmitter API
======================

.. doxygenfunction:: spdif_tx_reconfigure_sample_rate
.. doxygenfunction:: spdif_tx_output
.. doxygenfunction:: spdif_tx_shutdown


|appendix|

Known Issues
************

   * None

