USB MC audio - ADC pin lost MSB 4bit when BCLK/LRCK = 100

Technical questions regarding the XTC tools and programming with XMOS.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

USB MC audio - ADC pin lost MSB 4bit when BCLK/LRCK = 100

Post by xchips »

I'm currently using xk-216-mc board to implement some non-standard OSR I2S cases. In one case:
MCLK = 24MHz, BCLK = 4.8MHz, LRCK = 48kHz (i.e. BCLK/LRCK = 100, the ref design is 64x), DAC part can work correctly, but ADC lose the MSB 4-bit every time.

I didn't modify any port definition about I2S bus, PLL chip output a 24MHz clock as MCLK, and modified the 'MCLK_48' to 24000000 in customdefines.h.

The pin DAC0 is shortened with ADC0, and output test pattern data 0x12345678 from DAC0, when BCLK/LRCK = 96 (also a non-standard case, use the same code with case: BCLK/LRCK =100), I can get 0x12345678 back from ADC0. But when BCLK/LRCK = 100, I got 0x23456780 back from ADC0, the MSB 4bit had gone.

Please find the source code in the attachment (The file is too long, please just focus on deliver() function).
Thanks very much for your help!
You do not have the required permissions to view the files attached to this post.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

The resource usage of my project are as below:

Constraint check for tile[0]:
Cores available: 8, used: 4 . OKAY
Timers available: 10, used: 4 . OKAY
Chanends available: 32, used: 7 . OKAY
Memory available: 262144, used: 30988 . OKAY
(Stack: 2500, Code: 24004, Data: 4484)
Constraints checks PASSED.
Constraint check for tile[1]:
Cores available: 8, used: 4 . OKAY
Timers available: 10, used: 6 . OKAY
Chanends available: 32, used: 24 . OKAY
Memory available: 262144, used: 46812 . OKAY
(Stack: 2956, Code: 25912, Data: 17944)

So I think MCU should have power to handle input data. And I can get correct test pattern back for only 1 channel if I use the 'clearbuf(p_i2s_adc[index++]' function to clear the port FIFO, at this time, the other channel will be 0 all the time.

I don't know if I can have a chance to observe the FIFO data, I want to find out the missing data.
In fact, I have no idea on how to check this issue now, what I'm doing is to try the methods what I just came out.

I know the file 'audio.xc' is too long that it should bother you, but if someone has interest or wants to help, I will be so grateful.
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1164
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

So I think MCU should have power to handle input data.
Certainly - the I2S task sits idle much of the time. All it does is gather data from the host over a channel, output LRCLK, input from ADC pins to memory and output from memory to DAC pins. Also, you have 100Mhz allocated for this task currently which is probably an order of magnitude more than needed. Saying that though - I notice you have % (modulo) operators in the inner loop for testing for modulo 64. Best to pre-compute those outside and set a flag or use bit masks... Same goes for / .. either use >> for / powers of 2 of move outside. Divide and remainder take 32 cycles (as long as shared divider unit is free).
I don't know if I can have a chance to observe the FIFO data, I want to find out the missing data.
In fact, I have no idea on how to check this issue now, what I'm doing is to try the methods what I just came out.
Strictly speaking it's a simple buffer (transfer register) and shift register. Anyhow, you will need to run it on the simulator (with vcd viewer) to see these. The sim can show you *everything* and you can do the same loopback stuff on pins too. *everything* includes transfer register (the bit you read/write), shift register (the bit that connects to the port), full/empty flags and even if the port io operation is blocking the cpu etc. You will get full visibility this way. However, you will need to isolate your inner loop I2S code as the simulator is only really suitable for running small segments of your app (a few 100K cycles per second and lack of real world stimulus)

You can either write a traffic generator connected over a channel end to emulate the buffering side, or copy and paste the while(1) loop and make it run in a stand-alone app.
I know the file 'audio.xc' is too long that it should bother you, but if someone has interest or wants to help, I will be so grateful.
A trick here is to look in the .build_xxx directory (which is hidden). There you can find audio_io.xi. This is the pre-processed source and will strip out all of the #ifs (of which there are rather a lot)..

This can help you see what's really going on as you will get just the code that gets compiled.

Regarding:
I can get 0x12345678 back from ADC0. But when BCLK/LRCK = 100, I got 0x23456780 back from ADC0, the MSB 4bit had gone.
This suggests a shift rather than lost bits. Sounds like you are sampling late - Is it always 4b late for each consecutive loop? Or does it slip over time? If the former then it's a startup issue..
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

Hi, infiniteimprobability, thanks very much for your help!
Certainly - the I2S task sits idle much of the time.
Yes, I think so.
*everything* includes transfer register (the bit you read/write), shift register (the bit that connects to the port), full/empty flags and even if the port io operation is blocking the cpu etc. You will get full visibility this way.
This is a good news to me, since I'm new to XMOS, so unfortunately, I haven't got these skills..I need some time to learn them.
This can help you see what's really going on as you will get just the code that gets compiled.
Thanks, now I got this.
Sounds like you are sampling late - Is it always 4b late for each consecutive loop?
Yes, I think so, and I had tried some methods to get the missing 4b back, but for now, they didn't work.
And it always 4b late for each consecutive loop. But I'm sorry, what's the meaning of " it's a startup issue.."?

Thanks again.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

I had tried some new methods for debugging:
1. Use the VCD tracing function to observe the IO timing, I can see the timing in the while(1) loop if there is no delay before entering the while(1) loop. But I can't see the timing if there is 1 (or other values) second delay before entering the loop. Maybe I should learn more about VCD function.

2. Create a stand-alone app to run the I2S while(1) loop. And I found something new about this issue: In a stand-alone app, not every time (download *xe file to MCU) the missing bits is 4. It can be other random values...I even get correct pattern 0x12345678 back for only 1 time.

The while(1) loop:

Code: Select all

    while(1)
    {
        // Lch=============================================================
        // Read 32bit ADC0 data
        asm volatile("in %0, res[%1]" : "=r"(rev_sample)  : "r"(p_din[0]));
        // bitrev
        bitrev_sample   = bitrev(rev_sample);
        adc_data_array_Lch[lrck_cycle_cnt] = bitrev_sample;

        // Output 32bit LRCLK
        p_lrck <: 0x00000000;
        // Output 32bit DAC data
        p_dout[0] <: Lch_sample;

        // Read extra ADCbits for non-standard OSR and throw it directly
        asm("setpsc res[%0], %1"::"r"(p_din[0]), "r"(18));
        asm("in %0, res[%1]":"=r"(rev_sample):"r"(p_din[0]));
        // bitrev

        // Output extra LRCLK bits for non-standard OSR
        asm("setpsc res[%0], %1"::"r"(p_lrck), "r"(18));
        asm("out res[%0], %1"::"r"(p_lrck),"r"(0x00020000));

        // Output extra DAC bits for non-standard OSR
        asm("setpsc res[%0], %1"::"r"(p_dout[0]), "r"(18));
        asm("out res[%0], %1"::"r"(p_dout[0]),"r"(0x00000000));

        // Rch=============================================================
        // Read 32bit ADC0 data
        asm volatile("in %0, res[%1]" : "=r"(rev_sample)  : "r"(p_din[0]));
        // bitrev
        bitrev_sample   = bitrev(rev_sample);
        adc_data_array_Rch[lrck_cycle_cnt] = bitrev_sample;

        // Output 32bit LRCLK
        p_lrck <: 0xFFFFFFFF;
        // Output 32bit DAC data
        p_dout[0] <: Rch_sample;

        // Read extra ADCbits for non-standard OSR and throw it directly
        asm("setpsc res[%0], %1"::"r"(p_din[0]), "r"(18));
        asm("in %0, res[%1]":"=r"(rev_sample):"r"(p_din[0]));
        // bitrev

        // Output extra LRCLK bits for non-standard OSR
        asm("setpsc res[%0], %1"::"r"(p_lrck), "r"(18));
        asm("out res[%0], %1"::"r"(p_lrck),"r"(0x0001FFFF));

        // Output extra DAC bits for non-standard OSR
        asm("setpsc res[%0], %1"::"r"(p_dout[0]), "r"(18));
        asm("out res[%0], %1"::"r"(p_dout[0]),"r"(0x00000000));

        // For debugging
        if(lrck_cycle_cnt == 9)
        {
            for(unsigned i = 0; i < 10; i++)
                printf("i = %d, Lch sample = 0x%.8X, Rch sample = 0x%.8X\n", i, adc_data_array_Lch[i], adc_data_array_Rch[i]);
            break;
        }
        else
            lrck_cycle_cnt++;
    }
The whole stand-alone app for 'xCORE-200 MC AUDIO 2V0' board (Using build modules: lib_i2c(3.1.6) lib_xassert(2.0.1) lib_logging(2.0.1):
You do not have the required permissions to view the files attached to this post.
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1164
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

And it always 4b late for each consecutive loop. But I'm sorry, what's the meaning of " it's a startup issue.."?
Sorry - I didn't explain that fully. The way most xmos streaming interfaces are written is that the ports/clocks are all configured with the clock stopped. Often they have timed outputs/inputs scheduled, which are stalled because the clock is stopped and the port timers are frozen.
Then the clock is stated (which handily zeros the port clocks) and the scheduled initial port operations happen.
Following that, the code sits in a while loop emptying and filling port transfer buffers. As long as the output transfer register is filled before the shift register empties and the input transfer register is emptied before the shift register fills, then uninterrupted streaming happens for ever more..

So my comment was that, if the while loop always sees a consistent shift, then it's likely that the initialisation (initial port operations before clocks started) needs tweaking.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

Hi, infiniteimprobability, thanks for your help again.
then it's likely that the initialisation (initial port operations before clocks started) needs tweaking.
In ref design, the initialization is:
1. Configure clock and IOs.
2. Start clock.
Do you mean that I need to start clock first then configure IOs? i.e.
1. Configure clock.
2. start clock.
3. configure IOs.
I had tried this but it didn't work.

And I finally found a method to make BCLK/LRCK = 100 work:
The while(1) loop for I2S timing in ref design is:
(only Lch, Rch is similar with Lch)
Step 1: read 32bit ADC data back.
Step 2: output 32bit LRCK clock.
Step 3: output 32bit DAC data.

My while(1) loop for BCLK/LRCK = 100 before (Always lost MSB 4b):
Step 1: read 32bit ADC data back.
Step 2: output 32bit LRCK clock.
Step 3: output 32bit DAC data.
Step 4: read extra 18 bits ADC data back.
Step 5: output extra 18 bits LRCK clock.
Step 6: output extra 18 bits DAC.

New while(1) loop (can get test pattern back):
Step 1: output 32bit LRCK clock.
Step 2: output 32bit DAC data.
Step 3: read 32bits ADC data back.
Step 4: output extra 18bits LRCK clock.
Step 5: output extra 18bits DAC data.
Step 6: read extra 18bits ADC data back. Use ‘clearbuf(ADC port) instead.

Now I'm working on other cases: BCLK/LRCK = 50, 120, 200, 400 etc..I will update this thread if I have new found.

Thanks a lot.
Best regards.
User avatar
infiniteimprobability
Verified
XCore Legend
Posts: 1164
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

In ref design, the initialization is:
1. Configure clock and IOs.
2. Start clock.
Do you mean that I need to start clock first then configure IOs? i.e.
1. Configure clock.
2. start clock.
3. configure IOs.
I had tried this but it didn't work.
No - definitely meant the former as per the ref design. Always best to configure before starting the clock... like changing gears before you let the clutch out!
And I finally found a method to make BCLK/LRCK = 100 work:
Great.
The while(1) loop for I2S timing in ref design is:
(only Lch, Rch is similar with Lch)
Step 1: read 32bit ADC data back.
Step 2: output 32bit LRCK clock.
Step 3: output 32bit DAC data.

My while(1) loop for BCLK/LRCK = 100 before (Always lost MSB 4b):
Step 1: read 32bit ADC data back.
Step 2: output 32bit LRCK clock.
Step 3: output 32bit DAC data.
Step 4: read extra 18 bits ADC data back.
Step 5: output extra 18 bits LRCK clock.
Step 6: output extra 18 bits DAC.

New while(1) loop (can get test pattern back):
Step 1: output 32bit LRCK clock.
Step 2: output 32bit DAC data.
Step 3: read 32bits ADC data back.
Step 4: output extra 18bits LRCK clock.
Step 5: output extra 18bits DAC data.
Step 6: read extra 18bits ADC data back. Use ‘clearbuf(ADC port) instead.
The ordering is really important. If you let an output buffer empty or an input buffer fill when the transfer register is full the you'll miss data. If you try to read from a transfer register that is not full or write to a transfer register that is full, the core will block the I/O instruction until it can complete. That's why the simulator can be so useful as you can explicitly see the waiting (and resource name) signals, and get the order just right.
Now I'm working on other cases: BCLK/LRCK = 50, 120, 200, 400 etc..I will update this thread if I have new found.
Thanks - it's good to hear the successes and it will help other.
Thanks a lot.
No worries. You actually solved it yourself which is best as you have now gained a good understanding of xmos ports and clocks.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

Hi, infiniteimprobability.
No - definitely meant the former as per the ref design. Always best to configure before starting the clock... like changing gears before you let the clutch out!
Sorry, I think I'm not smart enough to understand what 'tweaking' mean..

And some updates for non-standard OSR cases:
For non-standard OSR design, in my while(1) loop for I2S timing;
A. Output and input the first 32-bit LRCK, DA_DAT, AD_DAT.
B. Output and input middle n times 32-bit LRCK, DA_DAT, AD_DAT.
C. Output and input (or use clearbuf(ADC port instead)) extra bits LRCK, DA_DAT, AD_DAT.

For BCLK/LRCK = :
1. 50, this case I need to output/input part of 32-bit LRCK/data (i.e. 25 bits for per channel)
My while(1) loop cannot work on this case, I got same data back but I cannot find test pattern
(even part of it) data after doing some left/right shift works for read back data.
This case still cannot work!
2. 100, C needs a clearbuf(ADC port) but not read back extra bits.
3. 120, the same with 100.
4. 200, I need to read the valid data back from B (For 200, for single channel, I will output/
input 32-bit datafirst, then output 2 extra 32-bit data, then at last (C), output/input extra
4bits data. i.e. 32 + 2*32 + 4 = 100 per channel).
5. 400, similar with 200.
6. 256, since 256/2%32 = 4, so I don't need C. I just need to output 0x80000000 for LCH(0x7FFFFFFF
for RCH) when output the 4th 32-bit LRCK, and the valid 32-bit ADC data just like 200 and 400,
it is at the 3rd 32-bit LRCK (i.e. (1) output/input 32bit; (2) output/input 32bit; (3) output/input
32-bit (found valid ADC data here); (4) output/input 32-bit).

Thanks for your help.
Regards.
xchips
Active Member
Posts: 51
Joined: Wed Jun 22, 2016 8:08 am

Post by xchips »

BCLK/LRCK = 50 issue now had been fixed.
The while(1) loop for this case (Rch is similar with Lch):
Lch: (BCLK/LRCK = 50)
Step 1: read 25bit ADC data back.
Step 2: output 25bit LRCK clock.
Step 3: output 25bit DAC data.
Step 4: Use ‘clearbuf(ADC port)’.