Simple I2S to I2S

If you have a simple question and just want an answer.
xfub
New User
Posts: 3
Joined: Sat Nov 12, 2016 12:05 am

Simple I2S to I2S

Post by xfub »

Dear XMOS folks,

being a newbie on XMOS I want to understand how to transfer audio data (to write the code) from a while(1) loop that reads I2S data to another thread (while(1) loop) that sends the I2S data. (24bits, Wclk = 64 BitClks)

The characteristic is that both threads are frequency and phase locked. In other words: Both threads have theire individual input pins for WordClock and BitClock but externally these pins are shorted.

The output thread looks like this:

Code: Select all

void OutThread (chanend adc_to_aes)

{

        .... init bla bla

        while(1)
        {
            set_thread_fast_mode_on();
            int dummy[2];
            while(1)
            {
                // output 24 bits on the rising or falling edge of the word clock (lrck)
                // note lr == 0 is right channel, lr == 1 is left channel
                dac_lrck when pinsneq(lr) :> lr @ t;
                switch(state[SOURCE_STATE])
                {

                    case AES_MUTE:

.....
                        break;

                    default:
                        t += 33;
                        if(lr == 0)
                        {
                            partout_timed(io_i2s[0], 24, dummy, t);
                            adc_to_aes :> dummy[0];
                        }
                        else
                        {
                            partout_timed(io_i2s[0], 24, dummy, t);
                            adc_to_aes :> dummy[1];
                        }
                        break;
                } // end switch(source)
            }
The input thread looks like this:

Code: Select all

        while(1)
        {
            int anabuf[2];
            set_thread_fast_mode_on();
            while(1)
            {
                adc_lrck when pinsneq(lra) :> lra @ ta;
                ta +=24;
                asm("setpt res[%0], %1" :: "r"(adc_data), "r"(ta));
                if(lra == 0)
                {
                    asm("in %0, res[%1]" : "=r"(anabuf[0])  : "r"(adc_data));
                    adc_to_aes <:anabuf[0];
                }
                else
                {
                    asm("in %0, res[%1]" : "=r"(anabuf[1])  : "r"(adc_data));
                    adc_to_aes <:anabuf[1];
                }
            }
        }


Can anybody tell my WHY this does not work?

Do I really have to go over 'shared' 'unsafe' memory?

Why can't I use a normal channel for the tranfer 'chanend adc_to_aes' to do this job?

(Remembering both threads are full synchronous and phase locked)

What do I NOT understand?

MANY THANKS!
Last edited by henk on Sat Nov 12, 2016 1:04 am, edited 1 time in total.
Reason: Inserted code tags to make post readable


henk
Respected Member
Posts: 347
Joined: Wed Jan 27, 2016 5:21 pm

Post by henk »

Hi xfub,
Can anybody tell my WHY this does not work?
Not for sure.

But there are a few things that may cause trouble.

On the output side I would be careful with the part-outs; Given it is I2S, it is much easier to out the whole set of data, shifted up by 8 bits if necessary? Assuming the port is buffered that would make it obvious that there is sufficient slack in the system to go around the loop.

I would also make sure that in the MUTE case you output a word of zeroes; you need to output something on I2S.

Also - I am assuming that you are an I2S slave, Ie, the BCLK and LRCLK are supplied on both, and you are somehow locked to the LRCLK

On the input side - avoid using setpt with a +24 (this will break it). The first time you get the correct bits, but you input 32 bits; the second time you will be late and it will wait for the port counter to wrap around (65536+24-32 = 65528 cycles).
Do I really have to go over 'shared' 'unsafe' memory?

Why can't I use a normal channel for the tranfer 'chanend adc_to_aes' to do this job?
No need for any shared memory - just pass the values through a (streaming) channel. It may have to be streaming, as you may not want a hard-synced-handshake; a streaming channel provides a very minimal elastic buffer - it just decouples the two threads.

However, if the two are completely locked (i.e., they share an LRCLK that comes in), it will work like a dream if you handover each word immediately after you get it. Ie, the input task will input the first (left?) word and pass it to the other task, who will receive it just after it has output the second (right) word, but in time for outputting the third (left) word. So you will get a single sample delay between input and output.

I haven't tried the code below (and you will need to add the muting etc), but this may do the trick. First figure out where to start, and make sure that both tasks agree on that. Now the input task starts 64 cycles before the output task (the one sample delay), they both need to start on the next LR cycle because we have just missed this one. Then never resent on the port counter - we are now locked to the LRCLK.

input:

Code: Select all

  lrclk when pinseq(1) :> int one @ t;
  toOuptut <: t;
  t += 64;
  asm("setpt res[%0], %1" :: "r"(adc), "r"(t));
  while(1) {
    adc :> left;
    toOutput <: left;
    adc :> right;
    toOutput <: right;
  }
output:

Code: Select all

  fromOuptut :> t;
  t += 128;
  asm("setpt res[%0], %1" :: "r"(dac), "r"(t));
  while(1) {
    fromOutput <: left
    dac <: left;
    fromOutput <: right;
    dac <: right;
  }
xfub
New User
Posts: 3
Joined: Sat Nov 12, 2016 12:05 am

Post by xfub »

Hi Henk,

now I see what caused all of my trouble:

"On the input side - avoid using setpt with a +24 (this will break it). The first time you get the correct bits, but you input 32 bits; the second time you will be late and it will wait for the port counter to wrap around (65536+24-32 = 65528 cycles)."

The code where I started from called another function with
adc_lrck when pinsneq(lra) :> lra @ ta;
ta +=24;
// Found inside of the function....!!!!
stop_clock(clk4);
stop_port(adc_data);
configure_in_port_no_ready(adc_data, clk4);
start_clock(clk4);
asm("setpt res[%0], %1" :: "r"(adc_data), "r"(ta));
if(lra == 0)
{
asm("in %0, res[%1]" : "=r"(anabuf[0]) : "r"(adc_data));
......

inside.

So restarting of the clk did the trick....

Thank you very much for opening my eyes, Henk!!