streaming chan communication problem [pls help]

Technical questions regarding the XTC tools and programming with XMOS.
asdf234
New User
Posts: 3
Joined: Wed Nov 04, 2015 4:40 pm

streaming chan communication problem [pls help]

Post by asdf234 »

Hello,

I use a sliceKIT Starter Kit with the Audio Slice Card (XA-SK-AUDIO). I use the "I2S Master sliceKIT Loopback Demo" to read Audio in (e.g. by an mp3 player) and to give it back out as well (e.g. boxes or headphones). This is working fine. What I want to do now is to use that ADC values and send them to another thread to be able to display their values (in future spectrum) on the colour display. This would be the "app_display_spectrum_demo".

I display now the most important program parts from the two demos I mentioned above, which I combined in one program. If you want to have a look at the full program you can easily look at the mentioned demos in the XSOFTip. If someone is interested to look into the full program I could also upload it. The problem is mentioned at the bottom of this post in bold.

the main looks like this:

Code: Select all

int main(){
	chan c_dc, c_lcd, c_sdram;
	streaming chan c_sig1; 
	streaming chan c_con; // , c_data, c_sig2;

	par {
		on tile[0]: app(c_dc, c_sig1, c_con); //++ on tile[0]: app(c_dc, c_sig1, c_sig2);
		on tile[0]: display_controller(c_dc,c_lcd,c_sdram);
		on tile[0]: lcd_server(c_lcd,lcdports);
		on tile[0]: sdram_server(c_sdram,sdramports);

//		on tile[1]: sine_wave(c_sig1, FFT_POINTS);    //simulated sine wave running parallel to the input
//		on tile[1]: sine_wave(c_sig2, FFT_POINTS/4);

        on tile[1] :
                        {
                            unsigned mclk_bclk_div = MCLK_FREQ/(AUSAMP_FREQ * 64); //   (512*48000)/48000 *64= 32768
                            audio_hw_init(mclk_bclk_div);
                            audio_hw_config(AUSAMP_FREQ);

                            i2s_master(i2s_resources, c_sig1, mclk_bclk_div);
                        }
	}return 0;
}
The app(c_dc, c_sig1, c_sig2) function gets usually from two sine_wave() functions values sent over the two streaming channels c_sig1 and c_sig2, which displays their spectrum values then at the display.

The sine_wave() function outputs a value every 20us over the streaming channel and looks like this:

Code: Select all

void sine_wave(streaming chanend c_sine, short maxSampPerCyc)
{
	timer t;
	unsigned time;
	int data,data1;

	t :> time;
	
	while (1){
		for (int i=1; i<=maxSampPerCyc; i++){
			data = 2048*sin(2*PI*i/maxSampPerCyc);
			data1 = data;
			t when timerafter(time+(XS1_TIMER_HZ/SAMP_FREQ)):> void; //:> time; SAMP_FREQ 1sec/50000=20us/// 100000 timer ticks = 1ms
			c_sine <: data1;
		}
	}
}
The app() function gets this values from two sine_wave() functions for example and looks like this:

Code: Select all

void app(chanend c_dc, streaming chanend c_sig1, streaming chanend c_sig2)
{
  unsigned frBufIndex=0, frBuf[2];
  unsigned magSpec[FFT_POINTS], maxSpec;

  // Create frame buffers
  frBuf[0] = display_controller_register_image(c_dc, LCD_ROW_WORDS, LCD_HEIGHT);
  frBuf[1] = display_controller_register_image(c_dc, LCD_ROW_WORDS, LCD_HEIGHT);
  display_controller_frame_buffer_init(c_dc, frBuf[0]);

  // Display spectrum periodically
  while (1){
	  frBufIndex = 1-frBufIndex;
	  magnitude_spectrum(c_sig1, c_sig2, magSpec);

	  maxSpec = 0;
	  for (int i=0; i<FFT_POINTS/2; i++)
		  maxSpec = (magSpec[i]>maxSpec)? magSpec[i]:maxSpec;
	  level_meter(c_dc, frBuf[frBufIndex], magSpec, FFT_POINTS/2, maxSpec);
	  display_controller_frame_buffer_commit(c_dc,frBuf[frBufIndex]);
  }

}
Within the app() function the magnitude_spectrum() function gets the streaming channel signals c_sig1 and c_sig2 and looks like this:

Code: Select all

void magnitude_spectrum( streaming chanend c_sig1, streaming chanend c_sig2, unsigned magSpectrum[]) 
{
	int sig_re[FFT_POINTS] = { 0 },  im[FFT_POINTS] = { 0 }, temp = 0; //   int sig_re[FFT_POINTS],  im[FFT_POINTS], temp;

	for (int i=0; i<FFT_POINTS; i++){

		// buffering
	    c_sig1 :> sig_re[i];
	    c_sig2 :> temp;

		// Mixing
		sig_re[i] += temp;
		im[i] = 0;
	}

	// fft
	fftTwiddle(sig_re, im, FFT_POINTS);
	fftForward(sig_re, im, FFT_POINTS, FFT_SINE);

	// magnitude spectrum
	for (int i=0; i<FFT_POINTS; i++)
		magSpectrum[i] = sig_re[i]*sig_re[i] + im[i]*im[i];

}
This is so far the old version of the program. A short summary: Test values are sent via two sine_wave() to app() and are displayed on the LCD.

Now to the second part where the audio input is captured (ADC) and right away sent to output it again over the DAC. This happens in the above already displayed main program on tile[1] in the function i2s_master(), which looks like this:

Code: Select all

void i2s_master(r_i2s &r_i2s, streaming chanend c_data, unsigned mclk_bclk_div)//streaming
{
    if(mclk_bclk_div == 1)
    {
        // TODO
    }
    else
    {
        // clock block 1 clocked off MCK
        set_clock_src(r_i2s.cb1, r_i2s.mck);
        // clock block 2 clocked off BCK (which is generated on-chip)
        set_clock_src(r_i2s.cb2, r_i2s.bck);
        // BCK port clocked off clock block 1
        set_port_clock(r_i2s.bck, r_i2s.cb1);
        // WCK and all data ports clocked off clock block 2
        set_port_clock(r_i2s.wck, r_i2s.cb2);

        for (int i = 0; i < I2S_MASTER_NUM_PORTS_ADC; i++)
        {
            set_port_clock(r_i2s.din[i], r_i2s.cb2);
        }
        for (int i = 0; i < I2S_MASTER_NUM_PORTS_DAC; i++)
        {
            set_port_clock(r_i2s.dout[i], r_i2s.cb2);
        }
        // Start clock blocks after configuration
        start_clock(r_i2s.cb1);
        start_clock(r_i2s.cb2);
    }
    // Run I2S i/o loop
    i2s_master_loop(r_i2s.din, r_i2s.dout, c_data, r_i2s.wck, r_i2s.bck, mclk_bclk_div);

    // Client must have killed us, so die..
}
The ADC and DAC values come from i2s_master_loop() at the very end and it looks like this:

Code: Select all

void i2s_master_loop(in buffered port:32 p_i2s_adc[], out buffered port:32 p_i2s_dac[], streaming chanend c, out buffered port:32 p_lrclk, out buffered port:32 p_bclk, int divide)
{
        unsigned sampsAdc[I2S_MASTER_NUM_CHANS_ADC];
        unsigned sampsDac[I2S_MASTER_NUM_CHANS_DAC];
        unsigned int data1,data2;

    /* Init sample buffers */
    for (int i = 0; i < I2S_MASTER_NUM_CHANS_ADC; i++)
    {
        sampsAdc[i] = 0;
    }
    for (int i = 0; i < I2S_MASTER_NUM_CHANS_DAC; i++)
    {
        sampsDac[i] = 0;
    }

    /* Lets do some I2S.. */
    // inputs and outputs are 32 bits at a time
    // assuming clock block is reset - initial time is 0
	// split SETPT from IN using asm - basically a split transaction with BCK generation in between
    // input is always "up to" given time, output is always "starting from" given time
	// outputs will be aligned to WCK + 1 (first output at time 32, WCK at time 31)
	// inputs will also be aligned to WCK + 1 (first input up to time 63, WCK up to time 62)

    for (int i = 0; i < I2S_MASTER_NUM_PORTS_DAC; i++)
    {
        p_i2s_dac[i] @ 64 <: 0;
    }

    for (int i = 0; i < I2S_MASTER_NUM_PORTS_ADC; i++)
    {
        asm("setpt res[%0], %1" :: "r"(p_i2s_adc[i]), "r"(63));
    }

    p_lrclk @ 31 <: 0;

    // clocks for previous outputs / inputs
    bck_32_ticks(p_bclk, divide);
    p_lrclk <: 0;
    bck_32_ticks(p_bclk, divide);

#pragma unsafe arrays

    while (1)
    {
        int p = 0;

        /* Send ADC samples over channel... */
[b]#pragma loop unroll
        for (int i = 0; i < I2S_MASTER_NUM_CHANS_ADC; i++){  //SOURCE
            sampsDac[i] = sampsAdc[i];    

     //       c <: sampsAdc[i];
           data1 =  sampsAdc[0];
           data2 =  ((data1)/2097152);
           c <: data2;
        }[/b]

        /* Receive DAC samples from channel... */
/*++#pragma loop unroll
        for (int i = 0; i < I2S_MASTER_NUM_CHANS_DAC; i++)
            c :> sampsDac[i];

        // input audio data
        // will be output to channel end as left-aligned
        // compiler would insert SETC FULL on DIN input, because it doesn't know about inline SETPT above
        // hence we need inline IN too
        p = 0;
        ++*/
#pragma loop unroll
        for (int i = 0; i < I2S_MASTER_NUM_CHANS_ADC; i+=2)
        {
            int x;
            asm("in %0, res[%1]" : "=r"(x)  : "r"(p_i2s_adc[p++]));
            sampsAdc[i] = bitrev(x);
        }

        /* Output LR clock to port */
        p_lrclk <: 0;

        /* drive bit clock */
        bck_32_ticks(p_bclk, divide);

        /* Output next DAC audio data for "Left" or "even" channels to I2S data ports.
         * Samples expected to come from channel end as left-aligned
         */
        p = 0;
#pragma loop unroll
        for (int i = 0; i < I2S_MASTER_NUM_CHANS_DAC; i+=2)
        {
            p_i2s_dac[p++] <: bitrev(sampsDac[i]);
        }

        /* Input previous ADC audio data
         * Will be output to channel end as left-aligned
         * compiler would insert SETC FULL on DIN input, because it doesn't know about inline SETPT above
         * hence we need inline IN too
         */
        p = 0;
#pragma loop unroll
        for (int i = 1; i < I2S_MASTER_NUM_CHANS_ADC; i+=2)
        {
            int x;
            asm("in %0, res[%1]" : "=r"(x)  : "r"(p_i2s_adc[p++]));
            sampsAdc[i] = bitrev(x);
        }

        /* Output LR clock value to port */
        p_lrclk <: 0xffffffff;

        /* drive bit clock. This will clock out LRClk and DAC data from ports and clock in next
         * ADC data into ports
         */
        bck_32_ticks(p_bclk, divide);

        /* Output "right" (or "odd") channel DAC data to DAC ports */
        p = 0;
#pragma loop unroll
        for (int i = 1; i < I2S_MASTER_NUM_CHANS_DAC; i+=2)
        {
            p_i2s_dac[p++] <: bitrev(sampsDac[i]);
        }

    }
}
Just for better display here the short version of code which is included in the above code:

Code: Select all

#pragma loop unroll
        for (int i = 0; i < I2S_MASTER_NUM_CHANS_ADC; i++){  //SOURCE
            sampsDac[i] = sampsAdc[i];    

     //       c <: sampsAdc[i];
           data1 =  sampsAdc[0];
           data2 =  ((data1)/2097152);
           c <: data2;
        }
This line puts the ADC values right away back to the DAC and ensures the audio on the speaker: sampsDac [ i ] = sampsAdc [ i ];

This works also fine.
I'm sorry this is so long, but I decided when I post it that I rather post the whole thing so it might help someone who really wants to try and help me.

A short summary and then finally the problem: We can give values over app() to the display and we can input and output audio files.

The problem: I would now like to send the ADC values from sampsAdc over a streaming channel to app() so they are displayed on the display. But so far whatever I tried, that moment when I send the ADC values (over e.g. c_sig1)

Code: Select all

i2s_master(i2s_resources, c_sig1, mclk_bclk_div);
to

Code: Select all

on tile[0]: app(c_dc, c_sig1, c_con);
the audio output stops (the sampsAdc[ i ] array is just always filled with zeroes).
I think this might be a timing problem since the app() function takes a while to accept the next value. The sampling from the audio values is set to 48000 what should provide a value every 20,3us.
Any sort of help, tip or thought on that problem would be appreciated. Thanks.
peter
XCore Addict
Posts: 230
Joined: Wed Mar 10, 2010 12:46 pm

Post by peter »

Have I missed something, but it looks to me that you haven't connected up the second channel (c_con) to your app, and hence when in the processing loop you input from both channels that one will lock up because nothing is providing data on c_con. After that the entire application will lock up and the audio will stop.
asdf234
New User
Posts: 3
Joined: Wed Nov 04, 2015 4:40 pm

Post by asdf234 »

peter wrote:Have I missed something, but it looks to me that you haven't connected up the second channel (c_con) to your app, and hence when in the processing loop you input from both channels that one will lock up because nothing is providing data on c_con. After that the entire application will lock up and the audio will stop.
First of all, thanks for looking into this problem.
The code I posted above was just one version where I tried to simply explain the problem. At the start I was always sending two signals to app(), like I will show in the code plot below, but I think it is not causing any blocking when a streaming channel is let open.

Here is what happens when app() gets two signals:

Code: Select all

int main(){
	chan c_dc, c_lcd, c_sdram;
	streaming chan c_sig1, c_sig2;

	par {

		on tile[0]: app(c_dc, c_sig1, c_sig2); 
		on tile[0]: display_controller(c_dc,c_lcd,c_sdram);
		on tile[0]: lcd_server(c_lcd,lcdports);
		on tile[0]: sdram_server(c_sdram,sdramports);

		on tile[1]: sine_wave(c_sig2, FFT_POINTS/4);

        on tile[1] : 
                        {
                            unsigned mclk_bclk_div = MCLK_FREQ/(AUSAMP_FREQ * 64); 
                            audio_hw_init(mclk_bclk_div);
                            audio_hw_config(AUSAMP_FREQ);

                            i2s_master(i2s_resources, c_sig1, mclk_bclk_div); 
                        }
	}
	return 0;
}
In this case app gets the audio data over c_sig1 and the sine_wave data over c_sig2. The result is still the same. The audio data array is empty (contains zeros) as soon as i2s_master is connected to app() over c_sig1. The data sent from sine_wave to app() is on the other hand displayed on the LCD. This is why I only used one streaming channel to app(), cause it works also with one.

I'm happy for any suggestions or hints.