Can i put multiple ADCs and DACs on different tile with xu224 or xu232

Sub forums for various specialist XMOS applications. e.g. USB audio, motor control and robotics.
susanyin0501
Active Member
Posts: 45
Joined: Thu Apr 20, 2017 9:00 am

Can i put multiple ADCs and DACs on different tile with xu224 or xu232

Post by susanyin0501 »

Dear all
we have a 16 analog input ports and 16 analog output ports audio device project, can we do it with xmos xu224 or xu232 chip?

16 analog input ports need 8 data pins ( ADC0-ADC7), it need 8 1bit ports on xu224 or xu232, same as 16 analog output ports need 8 1bit ports too.

so we can not place 2 ADC( AD0-AD3) and 2 DAC (DAC0-DAC3) on the same tile, As it is not enough 1bit pin on ervery tile.

Can i put 1 DAC and 1 ADC on one tile, the rest ADC and DAC on other tile ? would you please give me some ideas? Thanks


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

Post by infiniteimprobability »

Hmm - that is a lot of input output. Could you use TDM instead of I2S? That will get you down to 2 data lines in each direction. That would be the least work..
The issue is that there are maximum 16x 1b ports per tile and I2S has been written to assume it runs on a single tile. With BCLK and LRCLK that would limit you to 14 input/output..

What you are asking can be done.. bit it needs a little work. There are 3 options I can think of:

1) Have 2 x audio tasks running. Each can handle half of the I/O. I have seen this done before but you will need to do some re-naming of the globals in audio.xc for the second file and then handle either forwarding of data/commands from one to the other or modify decouple.xc to talk to 2 audios...
2) Use 4b ports instead of 1b ports for data lines. There is a prototype implementation of this but it's not an official release. I don't see why we couldn't share without support however..
3) Use some sort of bridge/port forwarding from TDM to I2S. It can be done but feels messy and would likely have unwanted delay issues..

What's your max sample rate and which chip is the I2S master?
susanyin0501
Active Member
Posts: 45
Joined: Thu Apr 20, 2017 9:00 am

Post by susanyin0501 »

Thanks for your reply, infiniteimprobability
1st solution, a lot of works need to do, and it will not be stable too, i think
2nd one, how can we get this unofficial release?
the last one, maybe it can't meet project latency time requirment.

we'd like to use AKM AK4458 and AK5558, Max sample rat at least 96KHZ, had better 192KHZ
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Hi, I think solution 1 can be perfectly stable - the Xmos architecture allows for precise synchronisation (using channels or interfaces) and offers timing determinism so it can work. However there's a few days work to understand the code base and make the modifications.

Solution 2) is not turn key though. We have a piece of reasonably well verified code that supported a different channel count. It will need some modifying and testing by yourselves to ensure you are happy with it. So you will need to get your hands dirty to a certain extent because it's not a standard configuration of the reference design.

Before I share, can you answer the question "which chip is the I2S master?". This code assumes xmos=I2S master. Also 192kHz @ 16ch @ 24b cannot be transported over the USB ISO endpoint (64Mb bandwidth). You will either need to drop back to 16b/192kHz or 24b/96kHz due to that limitation.

<edit> - I have just looked at the code and it's optimised for 4ch out and 8ch in currently. It would take a fair bit of work to extend this to 16ch in/out and it will definitely be possible, but you'll have to get familiar with the ISA including the zip/unzip instructions. I am not sure it will support 192kHz at this configuration (it might), 96kHz feels safe. I can share on that basis?
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

I had a quick play with this and getting to 8i 8o was trivial and support 192kHz no problem. Looks like 16i 16o will work too but performance takes a hit. It will max out at 96kHz and will require a priority core (100MHz).
susanyin0501
Active Member
Posts: 45
Joined: Thu Apr 20, 2017 9:00 am

Post by susanyin0501 »

Hi infiniteimprobability.
Thanks for your reply,
Sorry for reply so late,
we are disscussing and researching this project, we plan to use xmos xu332 chip as I2S master. we'll need your suggestion and help again later. thank a lot.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

Here is some sample code for 16i16o using 4b ports. There are many disclaimers...

- This is NOT thoroughly tested. It seems to work OK in simulation at 96kHz with 25MHz MCLK (a bit higher than 24.576MHz) on loopback with 100, 83.33, 80, 75MHz core performance. It fails at 71.4MHz. You can call set_core_high_priority_on() before audio(c_audio, null, null, i_gpio); in main.xc to get 1/5 of the core speed.
- The core needs to be overclocked to 800MHz to get 192kHz to work, so no really chance at 500MHz. Maybe hand optimised ASM but that's a BIG project
- It has not been tested long term for stability
- DFU has not been tested
- Did I say it hadn't been properly tested? Sorry to labour the point but please satisfy yourself that it will meet your needs
- It is optimised for the same number of channels in/out
- It was quite a complex piece of SW to write - you have to be aware of the I2S spec, xmos port buffer operation and ISA at a deep level. I will not be able to offer detailed support on this due to my day job keeping me very busy!
- Saying that, please do feedback on how you get on..
- I still think 2 x audio tasks is not such a bad option, but you will need to put many days aside to do this (things like DFU get a bit tricky) and a bit of variable renaming..You could use one audio task to forward to another (although one will need to be slave). I have done this in the past and it works fine but it was not trivial
- You will need to set I2S_WIRES to 2 and something like:

Code: Select all

            <Port Location="XS1_PORT_4A"  Name="PORT_I2S_DAC0"/>
            <port Location="XS1_PORT_4B"  Name="PORT_I2S_DAC1"/>
            <Port Location="XS1_PORT_4C"  Name="PORT_I2S_ADC0"/>
            <Port Location="XS1_PORT_4D"  Name="PORT_I2S_ADC1"/>
Here it is, replace the relevant bits in audio.xc

Code: Select all

/* Added so that the transfer can be started earlier in the audio loop, hiding the interrupt latency of this channel */
static inline void StartSampleTransfer(chanend c_out, const unsigned underflowWord)
{
    outuint(c_out, underflowWord);
}

#pragma unsafe arrays
static inline unsigned DoSampleTransfer(chanend c_out, const int readBuffNo, const unsigned underflowWord)
{
    /* outint() disabled because this was started earlier in the audio loop to hide the interrupt latency*/
    // outuint(c_out, underflowWord);

    /* Check for sample freq change (or other command) or new samples from mixer*/
    if(testct(c_out))
    {
        unsigned command = inct(c_out);
#ifndef CODEC_MASTER
        if(dsdMode == DSD_MODE_OFF)
        {
            // Set clocks low
            p_lrclk <: 0;
            p_bclk <: 0;
        }
        else
        {
#if(DSD_CHANS_DAC != 0)
            /* DSD Clock might not be shared with lrclk or bclk... */
            p_dsd_clk <: 0;
#endif
        }
#endif
#if (DSD_CHANS_DAC > 0)
        if(dsdMode == DSD_MODE_DOP)
            dsdMode = DSD_MODE_OFF;
#endif
#pragma xta endpoint "received_command"
            return command;
    }
    else
    {
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
        for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
        {
            int tmp = inuint(c_out);
            samplesOut[i] = tmp;
        }
#else
        inuint(c_out);
#endif
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
#if  NUM_USB_CHAN_IN < I2S_CHANS_ADC
        for(int i = 0; i < NUM_USB_CHAN_IN; i++)
#else
        for(int i = 0; i < I2S_CHANS_ADC; i++)
#endif
        {
            if(readBuffNo)
                outuint(c_out, samplesIn_1[i]);
            else
                outuint(c_out, samplesIn_0[i]);
        }
        /* Send over the digi channels - no odd buffering required */
#pragma loop unroll
        for(int i = I2S_CHANS_ADC; i < NUM_USB_CHAN_IN; i++)
        {
            outuint(c_out, samplesIn_0[i]);
        }
#endif
    }

    return 0;

}

static inline void InitPorts_4b_port(unsigned divide)
{
    unsigned tmp;

    /* Clear I2S port buffers */
    clearbuf(p_lrclk);

    for(int i = 0; i < I2S_WIRES_DAC; i++)
    {
        clearbuf(p_i2s_dac[i]);
    }
    for(int i = 0; i < I2S_WIRES_ADC; i++)
    {
        clearbuf(p_i2s_adc[i]);
    }

    p_lrclk <: 0 @ tmp;
#define DELAY_START     100//Enough cycles to get to I2S inner loop
    unsigned out_data_ports_delay = tmp + DELAY_START + 24; //+24 because 4b port empties 4x quicker than 1b port so delay 3/4 of lrclock cycle
    unsigned in_data_ports_delay = out_data_ports_delay - 1 + 8; //-1 because we setup read for before clock. +8 because we want to lag reading by one word (and main loop blocks on in)

    unsigned lr_port_delay = tmp + DELAY_START;
    /* Since BCLK is free-running, setup outputs/inputs at a known point in the future */
#pragma loop unroll
    for(int i = 0; i < I2S_WIRES_DAC; i++)
    {
        p_i2s_dac[i] @ out_data_ports_delay <: 0;
    }

    p_lrclk @ lr_port_delay <: 0x7FFFFFFF;
#pragma loop unroll
    for(int i = 0; i < I2S_WIRES_ADC; i++)
    {
        asm("setpt res[%0], %1"::"r"(p_i2s_adc[i]),"r"(in_data_ports_delay));
    }
}





#define NUM_IO_PER_I2S_FRAME    8   //The number of input/outputs to 4b port over one I2S frame
//---------------------------------------------------------------------------------------------------
// Description: Helper function to build a full frame of input words from two previous partial frames
//     Returns: none
//      Inputs: raw_rx_words - doubled buffered array of 32b read input port values
//      Outputs: zipped_ins  - arranged array of read input port values
//
// Because reading is buffered, data is read after it is is sampled. WHen using 4b ports, there are
// a total of 8 reads/writes per i2s cycle. Reads 0..6 are captured in the current cycle and read 7
// is captured in the next cycle. Hence a double buffered raw_rx_word buffer is implemented.
// The complete 8 reads frame is assembled in the following cycle when read 7 is available. This is
// then unzipped and written to the PCM input array samplesIn_x. Naturally, this adds a one sample
// latency.
//---------------------------------------------------------------------------------------------------
#pragma unsafe arrays
static inline void build_raw_input_buffer(unsigned raw_rx_words[2][NUM_IO_PER_I2S_FRAME],
                                            unsigned zipped_ins[NUM_IO_PER_I2S_FRAME], unsigned writeBuffNo){
    zipped_ins[0] = raw_rx_words[!writeBuffNo][1];   //Input from last I2S loop
    zipped_ins[1] = raw_rx_words[!writeBuffNo][2];
    zipped_ins[2] = raw_rx_words[!writeBuffNo][3];
    zipped_ins[3] = raw_rx_words[!writeBuffNo][4];
    zipped_ins[4] = raw_rx_words[!writeBuffNo][5];
    zipped_ins[5] = raw_rx_words[!writeBuffNo][6];
    zipped_ins[6] = raw_rx_words[!writeBuffNo][7];
    zipped_ins[7] = raw_rx_words[writeBuffNo][0];    //Input from this I2S loop
}

//-----------------------------------------------------------------------------
// Description: Helper function to unzip read port values to PCM samples
//     Returns: none
//      Inputs: zipped_ins - array of input read 32b port values
//              writeBuffNo - current index of PCM input double buffer
//      Outputs: samplesIn_0/1 - array of PCM values
//-----------------------------------------------------------------------------
#pragma unsafe arrays
static inline void unzip_input_buffer_to_samplesIn_left(unsigned zipped_ins[NUM_IO_PER_I2S_FRAME],
                                            unsigned samplesIn_0[], unsigned samplesIn_1[], unsigned writeBuffNo){
    unsigned long long temp64_ins[2];
    unsigned int unzipped_ins[4];

    temp64_ins[0] = (unsigned long long)(zipped_ins[0]) << 32 | zipped_ins[1];      // 64 bits 4-bit packed data (MSBs)
    temp64_ins[1] = (unsigned long long)(zipped_ins[2]) << 32 | zipped_ins[3];      // 64 bits 4-bit packed data (LSBs)
    {unzipped_ins[0], unzipped_ins[1]} = unzip(temp64_ins[0], 1);                   // 2-bit packed words of MSBs
    {unzipped_ins[2], unzipped_ins[3]} = unzip(temp64_ins[1], 1);                   // 2-bit packed words of LSBs
    temp64_ins[0] = (unsigned long long)(unzipped_ins[0]) << 32 | unzipped_ins[2];  // 64-bits of 2-bit packed data (MSBs)
    temp64_ins[1] = (unsigned long long)(unzipped_ins[1]) << 32 | unzipped_ins[3];  // 64-bits of 2-bit packed data (LSBs)
    if(writeBuffNo)
    {
        {samplesIn_1[0], samplesIn_1[2]} = unzip(temp64_ins[0], 0);                 // Create 1-bit packed words (MSBs)
        {samplesIn_1[4], samplesIn_1[6]} = unzip(temp64_ins[1], 0);                 // Create 1-bit packed words (LSBs)
    }
    else
    {
        {samplesIn_0[0], samplesIn_0[2]} = unzip(temp64_ins[0], 0);                 // Create 1-bit packed words (MSBs)
        {samplesIn_0[4], samplesIn_0[6]} = unzip(temp64_ins[1], 0);                 // Create 1-bit packed words (LSBs)
    }
}

//-----------------------------------------------------------------------------
// Description: Helper function to unzip read port values to PCM samples
//     Returns: none
//      Inputs: zipped_ins - array of input read 32b port values
//              writeBuffNo - current index of PCM input double buffer
//      Outputs: samplesIn_0/1 - array of PCM values
//-----------------------------------------------------------------------------
#pragma unsafe arrays
static inline void unzip_input_buffer_to_samplesIn_right(unsigned zipped_ins[NUM_IO_PER_I2S_FRAME],
                                            unsigned samplesIn_0[], unsigned samplesIn_1[], unsigned writeBuffNo){
    unsigned long long temp64_ins[2];
    unsigned int unzipped_ins[4];

    temp64_ins[0] = (unsigned long long)(zipped_ins[4]) << 32 | zipped_ins[5];      // 64 bits 4-bit packed data (MSBs)
    temp64_ins[1] = (unsigned long long)(zipped_ins[6]) << 32 | zipped_ins[7];      // 64 bits 4-bit packed data (LSBs)
    {unzipped_ins[0], unzipped_ins[1]} = unzip(temp64_ins[0], 1);                   // 2-bit packed words of MSBs
    {unzipped_ins[2], unzipped_ins[3]} = unzip(temp64_ins[1], 1);                   // 2-bit packed words of LSBs
    temp64_ins[0] = (unsigned long long)(unzipped_ins[0]) << 32 | unzipped_ins[2];  // 64-bits of 2-bit packed data (MSBs)
    temp64_ins[1] = (unsigned long long)(unzipped_ins[1]) << 32 | unzipped_ins[3];  // 64-bits of 2-bit packed data (LSBs)

    if(writeBuffNo)
    {
        {samplesIn_1[1], samplesIn_1[3]} = unzip(temp64_ins[0], 0);                 // Create 1-bit packed words (MSBs)
        {samplesIn_1[5], samplesIn_1[7]} = unzip(temp64_ins[1], 0);                 // Create 1-bit packed words (LSBs)
    }
    else
    {
        {samplesIn_0[1], samplesIn_0[3]} = unzip(temp64_ins[0], 0);                 // Create 1-bit packed words (MSBs)
        {samplesIn_0[5], samplesIn_0[7]} = unzip(temp64_ins[1], 0);                 // Create 1-bit packed words (LSBs)
    }
}


//-----------------------------------------------------------------------------
// Description: Helper function to zip up output samples to 4b buffered ports
//     Returns: none
//      Inputs: samplesOut - array of input PCM samples
//      Outputs: zipped_outs - array of 32b values to be output to buffered port
//-----------------------------------------------------------------------------
#pragma unsafe arrays
static inline void zip_samplesOut_to_output_buffer_left(unsigned samplesOut[], unsigned zipped_outs[NUM_IO_PER_I2S_FRAME]){
    unsigned long long temp64_outs[2];

    /* Packs of 1 bit */
    temp64_outs[0] = zip(samplesOut[0], samplesOut[2], 0);      // Interleave L1 & L2
    temp64_outs[1] = zip(samplesOut[4], samplesOut[6], 0);      // Interleave L3 & L4
    zipped_outs[0] = (unsigned int)(temp64_outs[0] >> 32);      // MSB of [L1,L2] into 32bits
    zipped_outs[1] = (unsigned int)(temp64_outs[0] >>  0);      // LSB of [L1,L2] into 32bits
    zipped_outs[2] = (unsigned int)(temp64_outs[1] >> 32);      // MSB of [L3,L4] into 32bits
    zipped_outs[3] = (unsigned int)(temp64_outs[1] >>  0);      // LSB of [L3,L4] into 32bits

    /* Packs of 2 bits */
    temp64_outs[0] = zip(zipped_outs[0], zipped_outs[2], 1);    // Interleave MSB's of [L1,L2] with [L3,L4]
    temp64_outs[1] = zip(zipped_outs[1], zipped_outs[3], 1);    // Interleave LSB's of [L1,L2] with [L3,L4]
    zipped_outs[0] = (unsigned int)(temp64_outs[0] >> 32);      // MSBs of [L1,L2,L3,L4] into 32bits
    zipped_outs[1] = (unsigned int)(temp64_outs[0]      );      // MSBs of [L1,L2,L3,L4] into 32bits
    zipped_outs[2] = (unsigned int)(temp64_outs[1] >> 32);      // LSBs of [L1,L2,L3,L4] into 32bits
    zipped_outs[3] = (unsigned int)(temp64_outs[1]      );      // LSBs of [L1,L2,L3,L4] into 32bits
}

//-----------------------------------------------------------------------------
// Description: Helper function to zip up output samples to 4b buffered ports
//     Returns: none
//      Inputs: samplesOut - array of input PCM samples
//      Outputs: zipped_outs - array of 32b values to be output to buffered port
//-----------------------------------------------------------------------------
#pragma unsafe arrays
static inline void zip_samplesOut_to_output_buffer_right(unsigned samplesOut[], unsigned zipped_outs[NUM_IO_PER_I2S_FRAME]){
    unsigned long long temp64_outs[2];

    /* Packs of 1 bit */
    temp64_outs[0] = zip(samplesOut[1], samplesOut[3], 0);      // Interleave R1 & R2
    temp64_outs[1] = zip(samplesOut[5], samplesOut[7], 0);      // Interleave R3 & R4
    zipped_outs[0] = (unsigned int)(temp64_outs[0] >> 32);      // MSB of [R1,R2] into 32bits
    zipped_outs[1] = (unsigned int)(temp64_outs[0] >>  0);      // LSB of [R1,R2] into 32bits
    zipped_outs[2] = (unsigned int)(temp64_outs[1] >> 32);      // MSB of [L3,L4] into 32bits
    zipped_outs[3] = (unsigned int)(temp64_outs[1] >>  0);      // LSB of [L3,L4] into 32bits


    /* Packs of 2 bits */
    temp64_outs[0] = zip(zipped_outs[0], zipped_outs[2], 1);    // Interleave MSB's of [R1,R2] with [R3,R4]
    temp64_outs[1] = zip(zipped_outs[1], zipped_outs[3], 1);    // Interleave LSB's of [R1,R2] with [R3,R4]
    zipped_outs[4] = (unsigned int)(temp64_outs[0] >> 32);      // MSBs of [R1,R2,R3,R4] into 32bits
    zipped_outs[5] = (unsigned int)(temp64_outs[0]      );      // MSBs of [R1,R2,R3,R4] into 32bits
    zipped_outs[6] = (unsigned int)(temp64_outs[1] >> 32);      // LSBs of [R1,R2,R3,R4] into 32bits
    zipped_outs[7] = (unsigned int)(temp64_outs[1]      );      // LSBs of [R1,R2,R3,R4] into 32bits
}

#if (I2S_WIRES_DAC != I2S_WIRES_ADC)
#error "I2S_WIRES must be same for DAC and ADC"
#else
#define I2S_WIRES I2S_WIRES_DAC
#define NUM_CHANS_PER_WIRE (I2S_CHANS_DAC / I2S_WIRES_DAC)
#endif

// I2S delivery thread - Enhanced to support 4b ports for I2S datalines 
#pragma unsafe arrays
unsigned static deliver_4b_port(chanend c_out, chanend ?c_spd_out, unsigned divide, unsigned curSamFreq, chanend ?c_adc)
{

    // ------------------------------------------------------------------------
    // Local Variables
    // ------------------------------------------------------------------------

    unsigned command;
    unsigned readBuffNo = 0;
    unsigned writeBuffNo = 1;

    // These are the double buffered raw input words aquired from the ports
    unsigned raw_rx_words[I2S_WIRES][2][NUM_IO_PER_I2S_FRAME] = {{{0}}};

    // Initial sample transfer
    StartSampleTransfer(c_out, 0);
    command = DoSampleTransfer(c_out, readBuffNo, 0);  // Underflow word forced to 0
    if(command) return command;

    //configure the IO ports. This sets up the port timers so that all will be in sync for main loop
    InitPorts_4b_port(divide);

    while(1)
    {
        // Local Variables
        unsigned int zipped_ins[I2S_WIRES][NUM_IO_PER_I2S_FRAME];
        unsigned int zipped_outs[I2S_WIRES][NUM_IO_PER_I2S_FRAME];
        unsigned int unzipped_ins[I2S_WIRES][4];
        unsigned tmp_in[I2S_WIRES];

        for (int i = 0; i < I2S_WIRES; i++){
            zip_samplesOut_to_output_buffer_left(&samplesOut[i * NUM_CHANS_PER_WIRE], zipped_outs[i]); // Turn our samples into port vals
        }
        // --------------------------------------------------------------------
        // Change state of LRCLK to Low (LEFT)
        // LR clock delayed by one clock, This is so MSB is output on the falling edge of BCLK
        // after the falling edge on which LRCLK was toggled. (see I2S spec)
        // --------------------------------------------------------------------
        p_lrclk <: 0x80000000;


        // -----------------------------------------------------------------------------
        // Input and output 4 x 32b words
        // Note loop unrolled manually so we can schedule compute in the gaps between IO
        // -----------------------------------------------------------------------------
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][0]);      // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][0] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][1]);      // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            build_raw_input_buffer(raw_rx_words[i], zipped_ins[i], writeBuffNo);
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][1] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][2]);      // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            unzip_input_buffer_to_samplesIn_left(zipped_ins[i], &samplesIn_0[i * NUM_CHANS_PER_WIRE], &samplesIn_1[i * NUM_CHANS_PER_WIRE], writeBuffNo); // Turn our port vals into samples
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][2] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][3]);      // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            zip_samplesOut_to_output_buffer_right(&samplesOut[i * NUM_CHANS_PER_WIRE], zipped_outs[i]); // Turn our samples into port vals
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][3] = bitrev(tmp_in[i]);
        }


        // ------------------------------------------------------------------------
        // At this point we have just transmitted the latest LEFT data and built a
        // complete raw input buffer from this and last cycle
        // Unpack the received data and place in the appropriate samplesIn_X[] buffer
        // zipped_ins[] ordering is 0 = most significant ... 3 = least significant
        // ------------------------------------------------------------------------

        p_lrclk <: 0x7FFFFFFF;


        // -----------------------------------------------------------------------------
        // Input and output 4 x 32b words
        // Note loop unrolled manually so we can schedule compute in the gaps between IO
        // -----------------------------------------------------------------------------
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][0 + 4]);  // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][0 + 4] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][1 + 4]);  // Push 32 bits of data to the buffered out port
        }
        for (int i = 0; i < I2S_WIRES; i++){
            unzip_input_buffer_to_samplesIn_right(zipped_ins[i], &samplesIn_0[i * NUM_CHANS_PER_WIRE], &samplesIn_1[i * NUM_CHANS_PER_WIRE], writeBuffNo); // Turn our port vals into samples
        }
         for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][1 + 4] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][2 + 4]);  // Push 32 bits of data to the buffered out port
            if (i == 0) StartSampleTransfer(c_out, 0);               // Trigger interrupt in decouple.xc. Hides the 400ns ISR latency
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][2 + 4] = bitrev(tmp_in[i]);
        }

        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_dac[i] <: bitrev(zipped_outs[i][3 + 4]);  // Push 32 bits of data to the buffered out port
            if (i == I2S_WIRES - 1) {
                if(readBuffNo) command = DoSampleTransfer(c_out, 1, 0);    // Do sample transfer early to make use of IO gaps
                else           command = DoSampleTransfer(c_out, 0, 0);    // Underflow fixed at 0
                if(command) return command;
            }
        }
        for (int i = 0; i < I2S_WIRES; i++){
            p_i2s_adc[i] :> tmp_in[i];                      // Read in the ADC data now that clocks have run
        }
        for (int i = 0; i < I2S_WIRES; i++){
            raw_rx_words[i][writeBuffNo][3 + 4] = bitrev(tmp_in[i]);
        }

        // Would normally do sample transfer here, but we do it early in the previous gap. This is OK
        // because all values are buffered anyhow. Inputs used in next cycle, outputs already calculated from last cycle

        readBuffNo = !readBuffNo;   // Flip the ADC buffer
        writeBuffNo = !writeBuffNo;
    }
    return 0;
}
susanyin0501
Active Member
Posts: 45
Joined: Thu Apr 20, 2017 9:00 am

Post by susanyin0501 »

thanks infiniteimprobability
As you said " you have to be aware of the I2S spec, xmos port buffer operation and ISA at a deep level.", I'll learn and study all of these and your code.
if we start this project with this solution, i'll do feedback for you. Thanks again
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am
Contact:

Post by infiniteimprobability »

I'll learn and study all of these and your code.
Hope it's enjoyable! Here's the high level view.

- I2S is an endless loop which outputs DAC data to the port transfer buffers. Transfer buffer goes to shift register when the shift register empties.
- For the input, ADC data is shifted into the shift register which is written to the transfer register when it is full.
- Registers are 32b but pin data is 1b
- The goal is to prevent the output shift register from emptying and the input transfer register from filling for too long
- Standard I2S fills the outputs and then blocks on the inputs until they are ready (so input buffer is kept small). This happens twice per cycle
- For 4b I2S, it has to happen 8 times per cycle because we load in 4b at a time. We also have to unpack / pack the data. This happens using zip/unzip
- FOr good info on zip/unzip you can read this https://www.xmos.com/download/private/A ... rc1%29.pdf
- The simulator and waveform viewer are pretty much essential tools for developing this type of IP. It's closer to writing verilog than normal C!

Good luck!
susanyin0501
Active Member
Posts: 45
Joined: Thu Apr 20, 2017 9:00 am

Post by susanyin0501 »

Hi infiniteimprobability
is it right that sending 4bit data every time, need 8 times sending for 32 bits sample data in th above code ? on the other word, use 4b port pins instead of 1 bit pin transfer a sample data.

now my question, i'd like to transfer a sample data ( TDM mode, there is 16 channels data in every TDM frame for ADC or DAC ) with one bit ( P4C0, P4C1, P4C2, P4C4) of 4b port ( P4C port),

<Port Location="XS1_PORT_4A0" Name="PORT_I2S_DAC0"/> // just one bit of 4A port

<Port Location="XS1_PORT_4A" Name="PORT_I2S_DAC0"/> // not the whole port pins

what shall i do? thanks
Post Reply