I2S Master to I2S Slave problem Topic is solved

Technical questions regarding the XTC tools and programming with XMOS.
Post Reply
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

I2S Master to I2S Slave problem

Post by zhengyang0512 »

Recently, I am doing research about transfering the music from the I2S master mode on the one side to I2S slave mode on the other side at xmos.
I2S_master_to_I2S_slave.png
I2S_master_to_I2S_slave.png (14.85 KiB) Viewed 7244 times
I2S_master_to_I2S_slave.png
I2S_master_to_I2S_slave.png (14.85 KiB) Viewed 7244 times
My reference software is the AN00231: SPDIF Receive to I2S output using Asynchronous Sample Rate Conversion. This case uses the spdif receive module to input the music and output the music to I2S master module.
spdif_to_i2s(master).png
(57.22 KiB) Not downloaded yet
spdif_to_i2s(master).png
(57.22 KiB) Not downloaded yet
And I modify the above software in 2 steps:
First, I modify the spdif receive module into I2S master. By using the xscope tool, I have traced the sample at the trace window, the I2S master moduel works fine.

Code: Select all

on tile[AUDIO_TILE]: {  
       configure_clock_src(R18_XMOS_MCLK, R18_MCLK);
       start_clock(R18_XMOS_MCLK);
        i2s_master(R18_xmos_i2s, R18_data_out, 1, R18_data_in, 1, R18_BCLK, R18_LRCLK, R18_XMOS_BCLK, R18_XMOS_MCLK);
}
 

Code: Select all

void R18_to_xmos(server i2s_callback_if i2s,  client serial_transfer_push_if i_serial_in)
{
    while(1){
        select{
        case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
                i2s_config.mode = I2S_MODE_I2S;
                i2s_config.mclk_bclk_ratio = 8;//11.2896MHz or 2.8224MHz
                delay_milliseconds(2);
//                debug_printf("this is a test*********\n");
                break;

        case i2s.restart_check() -> i2s_restart_t restart:
                // This application never restarts the I2S bus
                restart = I2S_NO_RESTART;
                break;

            case i2s.receive(size_t index, int32_t sample):
                    //in_samps[index] = sample;
                    i_serial_in.push(sample, index);
                    //zhengyang
                    xscope_int(R18_LEFT, sample);
                    break;

            case i2s.send(size_t index) -> int32_t sample:
                    break;

        }
    }
}
Next, I modify the I2S master module into the I2S slave module.

Code: Select all

        on tile[USB_TILE]: {
            debug_printf("Starting I2S\n");
            i2s_slave(xmos_dsp_i2s, DSP_data_out, 1, DSP_data_in, 1, DSP_BCLK, DSP_LRCLK, XMOS_DSP_BCLK);

        }

Code: Select all

#define MUTE_MS_AFTER_SR_CHANGE   350    //350ms. Avoids incorrect rate playing momentarily while new rate is detected

//Shim task to handle setup and streaming of I2S samples from block2serial to the I2S module
#pragma unsafe arrays   //Performance optimisation of i2s_handler task
void xmos_to_dsp(server i2s_callback_if i2s, client serial_transfer_pull_if i_serial_out)
    {
    unsigned sample_rate = DEFAULT_FREQ_HZ_I2S;
    unsigned mclk_rate;
    unsigned restart_status = I2S_NO_RESTART;
    unsigned mute_counter; //Non zero indicates mute. Initialised on I2S init SR change

    while (1) {
        select {
            case i2s.init(i2s_config_t &?i2s_config, tdm_config_t &?tdm_config):
                if (!(sample_rate % 48000)) mclk_rate = MCLK_FREQUENCY_48;  //Initialise MCLK to appropriate multiple of sample_rate
                else mclk_rate  = MCLK_FREQUENCY_44;
                i2s_config.mclk_bclk_ratio = mclk_rate / (sample_rate << 6);
                i2s_config.mode = I2S_MODE_I2S;
                debug_printf("Initializing I2S to %dHz and MCLK to %dHz\n", sample_rate, mclk_rate);
                restart_status = I2S_NO_RESTART;
                mute_counter = (sample_rate * MUTE_MS_AFTER_SR_CHANGE) / 1000; //Initialise to a number of milliseconds
            break;

            //Start of I2S frame
            case i2s.restart_check() -> i2s_restart_t ret:
                ret = restart_status;
            break;

            //Get samples from ADC
            case i2s.receive(size_t index, int32_t sample):
            break;

            //Send samples to DAC
            case i2s.send(size_t index) -> int32_t sample:
                sample = i_serial_out.pull(index);
                if (mute_counter){
                    sample = 0;
                    mute_counter --;
                }
            break;

        }
    }
}
However, I can't see the trace data and the trace window print the below information without a stop.

Code: Select all

Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
Initializing I2S to 44100Hz and MCLK to 22579200Hz
......
That means my I2S slave module fell into circulation at the init process. The problem troubles me so much.
If anyone know the problem, please tell me, thank you very much!
This is the whole test project:
Rewrite_I2S.zip
(890.46 KiB) Downloaded 284 times
Rewrite_I2S.zip
(890.46 KiB) Downloaded 284 times


View Solution
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Hi, I wonder if the debug print is upsetting timing. I notice in your config.xscope file you have:

Code: Select all

<xSCOPEconfig ioMode="none" enabled="true">
Try:

Code: Select all

<xSCOPEconfig ioMode="basic" enabled="true">
and enable debug print over xscope (nice and fast)

If you look at the implementation of I2S slave in i2s_slave_impl.h you can see:

Code: Select all

        while (!syncerror && (restart == I2S_NO_RESTART)) {
If there is a synch error (which can be caused by slow debug print over JTAG) then I2S will restart and call the init callback again.
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

infiniteimprobability wrote:Hi, I wonder if the debug print is upsetting timing. I notice in your config.xscope file you have:

Code: Select all

<xSCOPEconfig ioMode="none" enabled="true">
Try:

Code: Select all

<xSCOPEconfig ioMode="basic" enabled="true">
and enable debug print over xscope (nice and fast)

If you look at the implementation of I2S slave in i2s_slave_impl.h you can see:

Code: Select all

        while (!syncerror && (restart == I2S_NO_RESTART)) {
If there is a synch error (which can be caused by slow debug print over JTAG) then I2S will restart and call the init callback again.
Thanks for reply, in the i2s slave program, the following code make the init callback restart again and again

Code: Select all

                   
if (mute_counter){
	sample = 0;
         mute_counter --;
}
Aslo, could you please give me some example code about the I2S slave mode? I have read the doc in lib_i2s, although there are some examples related to this, it's useless for my point. Thank you very much.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

the following code make the init callback restart again and again
if (mute_counter){
sample = 0;
mute_counter --;
}
That is strange. That code literally only zeroes samples until a threshold has been reached. I suspect it's more a case of tipping the balance on timing. The callbacks in the i2s handler all apply back pressure on the I2S loop. If that backpressure is too great (code execution too long) then it will lose synch and restart. Be careful about adding too much code to them - in and out quickly like you would an ISR is important.

Aslo, could you please give me some example code about the I2S slave mode? I have read the doc in lib_i2s, although there are some examples related to this, it's useless for my point. Thank you very much.
This is a good point. I can see no example in the repo or published component. I have added a bug against the library for that. The best I can point you to in the short term is the test code:

https://github.com/xmos/lib_i2s/tree/ma ... e_test/src


Out of interest, what sample rate are you running at? Is it always 44.1KHz?
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

Out of interest, what sample rate are you running at? Is it always 44.1KHz?
Now, my test sample rate is 44.1KHz, but my requirements are 44.1kHz ~ 192kHz. And thanks for your patience, I have already read the test code about the slave mode. However, it's difficult for me. And I also don't know the reason about my problem........
User avatar
zhengyang0512
Member++
Posts: 27
Joined: Tue Aug 23, 2016 6:14 am

Post by zhengyang0512 »

Oh, I found the two reasons which cause the problem:
First, the I2S slave clock should be stable. At the beginning of my test, I use the oscilloscope to test the signal of my crystal vibration source at BCLK and LRCLK. The two clock changes all the time, although the range is not very big. I choose a more stable crystal vibration source, and use a small test code. Then I2S slave code functions well.
Second, I use the attachment program to make a experiment, however, the code would crashed just for a while. And the console print some redundant information again and again. I try to comment the debug_printf code like this:

Code: Select all

            case i_fs_ratio.new_sr_notify():            //Notification from SR manager that we need to initialise ASRC
                in_fs_code = samp_rate_to_code(i_fs_ratio.get_in_fs());         //Get the new SRs
                out_fs_code = samp_rate_to_code(i_fs_ratio.get_out_fs());
//                debug_printf("New rate in SRC in=%d, out=%d\n", in_fs_code, out_fs_code);
                nominal_fs_ratio = asrc_init(in_fs_code, out_fs_code, asrc_ctrl, ASRC_CHANNELS_PER_INSTANCE, ASRC_N_IN_SAMPLES, ASRC_DITHER_SETTING);
            break;
To be a surprise, the program can transfer a song without a stop. I think I have solve the problem.
Post Reply