<<

ADAT Transmit

There are two modules that can produce an ADAT signal. The simplest module is a single thread that inputs samples over a channel and that outputs data on a 1-bit port. A more complex module has a thread that inputs samples over a channel and that produces an ADAT signal onto a second channel. Another thread has to copy this data from the channel onto a port. The latter is useful if the ADAT output port is, for example, on a different core. See the examples section on how to use this.

An identical protocol is used by both modules for inputting sample values to be transmitted over ADAT. The first word transmitted over the chanend should be the multiplier of the master clock (either 1024 or 512), the second word should be the SMUX setting (either 0 or 2), then there should be N x 8 words of sample values, terminated by an XS1_CT_END control token. If no control token is sent, the transmission process will not terminate, and an infinite stream of ADAT data can be sent.

The multiplier is the ratio between the master clock and the bit-rate; 1024 refers to a 49.152 Mhz masterclock, 512 assumes a 24.576 MHz master clock.

The output of the ADAT transmit thread has to be synchronised with an external flip-flop. In order to make sure that the flip-flop captures the signal on the right edge, the output port should be set up as follows:

set_clock_src(mck_blk, mck);        // Connect Master Clock Block to mclk pin
set_port_clock(adat_port, mck_blk); // Set ADAT_tx to be clocked from mck_blk
set_clock_fall_delay(mck_blk, 7);   // Delay falling edge of mck_blk
start_clock(mck_blk);               // Start mck_blk

API

void adat_tx(chanend c_data, chanend c_port)

Function that takes data over a channel end, and that outputs this in ADAT format onto a 1-bit port.

The 1-bit port should be clocked by the master-clock, and an external flop should be used to precisely align the edge of the signal to the master-clock.

Data should be send onto c_data using outuint only, the first two values should be The multiplier and the smux values, after that output any number of eight samples (24-bit, right aligned), and if the process is to be terminated send it an control token 1.

The data is output onto a channel, which a separate process should output to a port. This process should byte-reverse every word read over the channel, and then output the reversed word to a buffered 1-bit port.

Parameters:
  • c_data – Channel over which to send sample values to the transmitter
  • c_port – Channel on which to generate the ADAT stream
void adat_tx_port(chanend c_data, buffered out port:32 p_data)

Function that takes data over a channel end, and that outputs this in ADAT format onto a 1-bit port.

The 1-bit port should be clocked by the master-clock, and an external flop should be used to precisely align the edge of the signal to the master-clock.

Data should be send onto c_data using outuint only, the first two values should be The multiplier and the smux values, after that output any number of eight samples (24-bit, right aligned), and if the process is to be terminated send it an control token 1.

Parameters:
  • c_data – Channel over which to send sample values to the transmitter
  • p_data – 1-bit port on which to generate the ADAT stream

Example

Below we show two example programs: a program that uses the direct interface, and a program that uses an intermediate thread to output to the port.

Example of direct port code

The output port needs to be declared as a buffered port, and the master clock input must be declared as an unbuffered input port. A clock block is also required:

buffered out port:32 adat_port = XS1_PORT_1P;
in port mck = XS1_PORT_1O;
clock mck_blk = XS1_CLKBLK_2;

The ports need to be setup so that the output port is clocked of the master clock with a suitable delay (to enable the external flop to latch the signal). Do not forget to start the clock block, otherwise nothing shall happen:

void setupClocks() {
    set_clock_src(mck_blk, mck);
    set_clock_fall_delay(mck_blk, 7);   // XAI2 board, set to appropriate value for board.

    set_port_clock(adat_port, mck_blk);
    start_clock(mck_blk);
}

The data generator should first transmit the clock multiplier and the SMUX flags, prior to transmitting data. To terminate, send an END token:

void generateData(chanend c_data) {
    outuint(c_data, 512);    // master clock multiplier (1024, 256, or 512)
    outuint(c_data, 0);      // SMUX flag (0, 2, or 4)
    for (int i = 0; i < 1000; i++) {
        outuint(c_data, i);  // left aligned data (only 24 bits will be used)
    }
    outct(c_data, XS1_CT_END);
}

The main program simply forks the data generating thread and the transmitter in parallel in two threads. Prior to starting the transmitter, the clocks should be set up:

int main(void) {
    chan c_data;

    par {
        generateData(c_data);
        {
            setupClocks();
            adat_tx_port(c_data, adat_port);
        }
    }
    return 0;
}

Example of ADAT with an extra thread

The output port needs to be declared as a buffered port, and the master clock input must be declared as an unbuffered input port. A clock block is also required:

buffered out port:32 adat_port = XS1_PORT_1P;
in port mck = XS1_PORT_1O;
clock mck_blk = XS1_CLKBLK_2;

The ports need to be setup so that the output port is clocked of the master clock with a suitable delay (to enable the external flop to latch the signal). Do not forget to start the clock block, otherwise nothing shall happen:

void setupClocks() {
    set_clock_src(mck_blk, mck);
    set_clock_fall_delay(mck_blk, 7);   // XAI2 board, set to appropriate value for board.
    set_port_clock(adat_port, mck_blk);
    start_clock(mck_blk);
}

The thread that drives the port should input words from the channel, and output them with reversed byte order. Note that this activity of INPUT, BYTEREV and OUTPUT takes only three instructions and can often be merged with other threads; for example if there is an I2S thread that delivers data syncrhonised to the same master clock, then that thread can simultaneously drive the ADAT and I2S ports:

void drivePort(chanend c_port) {
    setupClocks();
    while (1) {
        adat_port <: byterev(inuint(c_port));
    }
}

The data generator should first transmit the clock multiplier and the SMUX flags, prior to transmitting data. To terminate, send an END token:

void generateData(chanend c_data) {
    outuint(c_data, 512);    // master clock multiplier (1024, 256, or 512)
    outuint(c_data, 0);      // SMUX flag (0, 2, or 4)
    for (int i = 0; i < 1000; i++) {
        outuint(c_data, i);  // left aligned data (only 24 bits will be used)
    }
    outct(c_data, XS1_CT_END);
}

The main program simply forks the data generating thread and the transmitter in parallel in two threads. Prior to starting the transmitter, the clocks should be set up:

int main(void) {
    chan c_data;
    chan c_port;

    par {
        generateData(c_data);
        adat_tx(c_data, c_port);
        drivePort(c_port);
    }
    return 0;
}