Does strobed input has a bug/design flaw?

Technical questions regarding the XTC tools and programming with XMOS.
jorsc
Member++
Posts: 21
Joined: Fri May 05, 2017 1:39 pm

Does strobed input has a bug/design flaw?

Post by jorsc »

I'm trying to input a high speed I2S (non-audio related) to xmos.

For various reasons I decided to use the strobe function essentially like below. Assume (just to get numbers) that the word_select signal has period of 1ms (1kHz). So obviously it will be high 500uS and low 500uS. Each sample is 16 bit so the "p_clock_in" is 32kHz.
So I'm reading a 16 bit sample while the strobe (word_select) signal is high as below.

Code: Select all

in buffered port:32 p_in = XS1_PORT_1A;
in port p_word_select = XS1_PORT_1B;
in port p_clock_in = XS1_PORT_1C;
clock clk = XS1_CLKBLK_1;

myReadTask() {
  configure_clock_src ( clk , p_clock_in ) ;
  configure_in_port_strobed_slave ( p_in , p_word_select , clk , 0) ;
  start_clock ( clk ) ;
  unsigned dataIn;
  timer tmr;
  unsigned tStart, tStop;
  
  while (1)  {
    tmr :> tStart;
    p_in :> dataIn;
    tmr :> tStop;
    printintln((tStop-tStart) / 100);
  }
}
I would expect to print 500(uS) (as this was the time word_select signal was high) but in my test I print 1000(us)! I.e. the "p_in :> dataIn" takes the full 32 clock cycles to complete. Even though the word_select signal is only active half that time.
So it seems that the strobe input only inputs 16 bits to the buffered port (as expected), but waits the full 32 cycles to return!

This of course waste 500uS that I intended to use for code that makes the strobe input useless...


User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Hi. The reason for the 32 clock cycles to complete is due to p_in being a buffered single bit port with a depth of 32.

The XMOS buffered ports allow for only specific depth values so consider to break up the buffered port depth.

Can you try the following (quick idea) and post your results?

Code: Select all

in buffered port:8 p_in = XS1_PORT_1A; // changed this to depth of 8 as 16 is not allowed
in port p_word_select = XS1_PORT_1B;
in port p_clock_in = XS1_PORT_1C;
clock clk = XS1_CLKBLK_1;

myReadTask() {
  configure_clock_src ( clk , p_clock_in ) ;
  configure_in_port_strobed_slave ( p_in , p_word_select , clk , 0) ;
  start_clock ( clk ) ;
  unsigned dataIn1;
  unsigned dataIn2;
  timer tmr;
  unsigned tStart, tStop;
  
  while (1)  {
    tmr :> tStart;
    p_in :> dataIn1; // read 1st 8 bits
    p_in :> dataIn2; // read 2nd 8 bits
    tmr :> tStop;
    printintln((tStop-tStart) / 100);
  }
}
jorsc
Member++
Posts: 21
Joined: Fri May 05, 2017 1:39 pm

Post by jorsc »

Thanks for input!
OK I will try, though even if this works, I'm a bit worried as my real bitclock is 32768 KHz. I.e. there only 30ns between each clock cycle, so there might not be time to store dataIn1, and execute 'p_in :>dataIn2'?
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

I'm a bit worried as my real bitclock is 32768 KHz.

Is your bit clock 32768khz = 32.768Mhz OR 32.768khz (real time clock (rtc) base crystal)?
jorsc
Member++
Posts: 21
Joined: Fri May 05, 2017 1:39 pm

Post by jorsc »

bclk is 32.768MHz and ws_clock is 1.024MHz, so pretty fast...

My idea was to invert ws_clock externally and feed that to a second pin. Then have two tasks reading dataIn as:

One task strobe on normal ws_clock and the other strobe on the inverted ws_clock. So they would alternate reading the dataIn giving each task some headroom.

Now I'm thinking a work around might be to divide ws_clock externally by 2 and feed this to two pins (one inverted). Then each task would read two 16bit samples into the port (32b) buffer at a time. That would be OK given that I have time to shift/split the 32bit result into two separate sample before sending to my fifo.
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Another possible solution:

Code: Select all

in buffered port:8 p_in = XS1_PORT_1A; // changed this to depth of 8 as 16 is not allowed for 1 bit buffered ports
in port p_word_select = XS1_PORT_1B;
in port p_clock_in = XS1_PORT_1C;
clock clk = XS1_CLKBLK_1;

out port toggle_port = XS1_PORT_1D; // select any free port pin that is available
int count;

myReadTask() {
  configure_clock_src ( clk , p_clock_in ) ;
  configure_in_port_strobed_slave ( p_in , p_word_select , clk , 0) ;
  start_clock ( clk ) ;
  unsigned dataIn1;
  unsigned dataIn2;
  timer tmr;
  unsigned tStart, tStop;
  
  while (1)  {

    p_word_select when pinseq(0) :> void; // wait till p_word_select trigger is 1

    toggle_port <:= 0 @ count; // read the port count value

    count+=8; // time in future for 2nd block stream of 8 bits are to be sampled

    tmr :> tStart; // may not have enough time to read this value but confirm

    p_in :> dataIn1; // read 1st 8 bits
    p_in @ count :> dataIn2; // read 2nd 8 bits when port_count is +8 from start

    tmr :> tStop;

    printintln((tStop-tStart) / 100);

  }
}
The key point is that there are many options with the features inside this CPU that should allow you to accomplish your required timing.
jorsc
Member++
Posts: 21
Joined: Fri May 05, 2017 1:39 pm

Post by jorsc »

Thanks for you input!

I found a different solution, not using strobed input. Turns out the i2s_frame_slave_impl.h of the official i2s_library had a pretty elegant boiler plate.
This implementation uses a 'in buffered port:32 p_lrck' And in the while loop p_lrck is matched alternating with:

Code: Select all

        unsigned expected_low  = (mode == I2S_MODE_I2S ? 0x80000000 : 0x00000000);
        unsigned expected_high = (mode == I2S_MODE_I2S ? 0x7fffffff : 0xffffffff);
As I need to read two 16 bit samples into a 32 bit buffered port, I deduced (mind the bit reversal) that my p_word_select (=p_lrck) would endup as a single value as:

Code: Select all

      const unsigned expectedWsVal  = 0x7fff8000;
This worked fine! And I can now read samples securely without any external additional hardware.
I'm rather impressed that I can read such high speeds with xmos.