Measuring short time intervals - again!

Technical questions regarding the XTC tools and programming with XMOS.
JohnR
Experienced Member
Posts: 93
Joined: Fri Dec 11, 2009 1:39 pm

Measuring short time intervals - again!

Post by JohnR »

Hi,
I am still struggling with measuring time delays in the few tens of nanoseconds
using my XK-1 board running with a 400Mhz reference clock. I now have altered
my pulse generator to combine the original start/stop pulses into a single pulse
generated via an XOR gate.

If I feed this pulse into my board that now is running only the function pulse_input()
shown in the code below, the minimum time delay I can see is about 80 nsecs. I
would like to get down to around 30 - 40 nsecs.

Code: Select all

void pulse_input(in port  start)//, streaming chanend c_start)
{
	int counter = 0;

	while (1) {
		while(peek(start)==1){
			test0 <: 1;
			counter++;
		}
		test0 <: 0;
		if(counter>0){
//			c_start <: counter;
			counter = 0;
		}
	}
}
The code below is my stripped down version that samples the input and if the input
is high, increments the counter and sets an output port high for the logic analyzer
(test0).

When the pulse goes low the output port is set low. The commented-out line allows
the data to be sent to another thread for accumulation and display. It is commented
out to keep this example as simple as possible.

The attached screen shot shows the start/stop signals (Channels 0 and 1) and the
output of the XOR chip (Channel2). Channel3 shows the test0 signal.

There is a delay of around 80 nsecs before test0 goes high. It stays high for the
duration of the input pulse which in this case is around 150 nsecs.

The problem starts when the pulse length is less than about 80 nsecs in which case
the pulse_input() function is never called. At the borderline length the function is
called intermittently.

I don't understand why there should be this long delay between the input pulse and the
corresponding input to pulse_input(). I have looked at the generated assembler and
there does not seem to anything odd there. Is there any more documentation on port
I/O than contained in "Programming XC on XMOS Devices"

Thanks again for any help.

John.
You do not have the required permissions to view the files attached to this post.


User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Hi John

Any reason for not using timers, e.g this sort of thing:

Code: Select all

#include <xs1.h>
#include <platform.h>
#include <stdio.h>

in port pulse = XS1_PORT_1K;

main(void){
  
  unsigned int begin = 0, end = 0;
  timer t ;
  
  while(1){
    select {
      case pulse when pinseq(1) :> void: {
        t :> begin;
        pulse when pinseq(0) :> void;
        t :> end;
        break;
      }
    }
    printf("Pulse width %d nanoseconds\n",10 * (end - begin));
  }
  
  return 0;
}
JohnR
Experienced Member
Posts: 93
Joined: Fri Dec 11, 2009 1:39 pm

Post by JohnR »

Hi Folknology,

Thanks for the quick and helpful response. Your code is similar to that I posted as a project a few days ago that used separate start and stop lines but I had not thought of putting both pinseqs in the same case statement.

Here is your method with the begin and end variables being added to the pinseq() function line as recommended by Woody.

Code: Select all

void pulse_input(in port  start)//, streaming chanend c_start)
{
  unsigned int begin = 0, end = 0;

  while(1){
    select {
      case start when pinseq(1) :> void @ begin: {
		test0 <: 1;
        start when pinseq(0) :> void @ end;
		test0 <: 0;
        break;
      }
    }
  }
}
I had tried earlier something similar to your code using separate pinseq(0) and pinseq(1) cases but the compiler objected to pinseq being used in two case statements.

I ran your version, the minimum delay measurable is a little better than mine, 66 ns rather than 80 ns but there is still the long delay between the input at the pin of the board and the test0 line going high - about 70 nsecs.

On page 113 of Programming XC on XMOS Devices the input is modelled in the function clkTimeStrobe() as a state machine. I should take a better look at this and see if it explains the delays I am seeing. I would really like to be able to measure pulse widths of less than 50 ns.

I did have a version running on an Actel FPGA that used strings of 2 and 3 input gates as a differential delay. I this case the problem was that I could not realistically measure delays greater than 10 or 15 nsecs!

Thanks again for your help.

John
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Yes Woody' is even better and using timestamps in that way is preferable to an actual timer.

if you remove the IO (test0) outputs and measure the diff between end and begin is that any better?
I just wondering if the output activity is having an effect and it would be useful to eliminate that.
JohnR
Experienced Member
Posts: 93
Joined: Fri Dec 11, 2009 1:39 pm

Post by JohnR »

Hi,
if you remove the IO (test0) outputs and measure the diff between end and begin is that any better?
I just wondering if the output activity is having an effect and it would be useful to eliminate that.
I have done this in the past - the test0 instruction seems to take only one cycle. The major delay seems to be the the case start line delay compared to the input pulse as seen by the logic analyzer.

Code: Select all

case start when pinseq(1) :> void @ begin: {
		test0 <: 1;
The @ begin/end variant does adds another instruction getts so thats another few nsec. I have never been sure that this getts really measures the time at which the input is sampled or whether is the time at which the getts instruction is performed.

Really the problem seems to be that the receipt of the pinseq(0) signal seems to abort the input transaction. This is OK if the pinseq(1) event has already been handled and the input is ready for the pinseq(0) signal as happens for pulse widths greater than 60+ nsecs. I will make a state diagram of the input code, clkTimeStrobe(), in Programming XC on XMOS Devices and see what it tells me.

Please keep your suggestions coming - I don't want to give up at this stage

John.
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Well one thing for sure should be that if there is a delay between pinseq(0) inputting and the @begin it will also be the same as pinseq(1) inputting and the @end timestamp so they would cancel each other out. It is interesting that the timestamp version adds cycles via the getts instruction however.

*update Also either of these allow you to add a timeout case to avoid the pinseq(1) state never arriving!
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Oh and FYI
I had tried earlier something similar to your code using separate pinseq(0) and pinseq(1) cases but the compiler objected to pinseq being used in two case statements.
The compiler is actually objecting to the same port appearing in more than one case, which is not allowed within a given select. I think it is the same for timers because Xmos limits condition detection on ports of timers to a single state for each resource.

I'm wondering if there is a setup delay for the second state condition pinseq(0). Is it possible to use 2 ports so that the second state is detected by a separate pin. Then you can use 2 cases one for each port? At least it would eliminate the possiblity.
JohnR
Experienced Member
Posts: 93
Joined: Fri Dec 11, 2009 1:39 pm

Post by JohnR »

Hi,
From your most recent post
Is it possible to use 2 ports so that the second state is detected by a separate pin. Then you can use 2 cases one for each port
That's more or less what I did in the version I posted earlier differing in that each port was viewed in a separate thread. I think I did at one time do a version as you suggest but I don't know now what the result was - probably negative as I did not pursue it.

From your earlier post
Well one thing for sure should be that if there is a delay between pinseq(0) inputting and the @begin it will also be the same as pinseq(1) inputting and the @end timestamp so they would cancel each other out.
That was my understanding when I started, however the problem is, I think, further upstream.

As I wrote earlier
Really the problem seems to be that the receipt of the pinseq(0) signal seems to abort the input transaction. This is OK if the pinseq(1) event has already been handled and the input is ready for the pinseq(0) signal as happens for pulse widths greater than 60+ nsecs.
Maybe the term abort is not quite correct but it does seem that if the end of pulse occurs before this 60 nsec time the pinseq() function handles the rising edge and does some processing (that takes some 20 nsec) and then because the falling edge has already occurred the function simply returns at that time.

Essentially it seems that if the input pulse falls before pinseq() has fully handled the rising edge, the trailing edge is ignored. I did some time ago try one of the delay functions without success but I think now it generates the delay further downstream so this input timing problem is not helped.

John
User avatar
Folknology
XCore Legend
Posts: 1274
Joined: Thu Dec 10, 2009 10:20 pm

Post by Folknology »

Maybe you could use a buffered in port and clock it using the pulse as a strobe. make the buffering 32bit to give max sample. Then count the 1's and multiply by clock period. something like this:

Code: Select all

in buffered port 32 seq  = XS1_PORT_1A;
in port pulse = XS1_PORT_1B;
clock clk = XS1_CLKBLK_1 

int main (void) {
   configure_clock_rate (clk, 100, 0);
   configure_in_port (seq, clk);
   configure_in_port_strobed_slave (seq, pulse, clk);
   start_clock ( clk );
   while (1) {      
     int x ;
     seq :> x ;
     count_hi_bits_for_width(x)
} }
This is sudo code, I have not tried it, it is there to convey the concept.
richard
Respected Member
Posts: 318
Joined: Tue Dec 15, 2009 12:46 am

Post by richard »

Hi,

My understanding (correct me if I'm wrong) is that you want to accurately measure the widths of a series of high pulses on a 1 bit port. To me the obvious way to do this would be to use conditional inputs and timestamps:

Code: Select all

#include <xs1.h>

void detector(in port p, streaming chanend c) {
	unsigned a, b, width;
	p when pinseq(0) :> void;
	while (1) {
		p when pinseq(1) :> void @ a;
		p when pinseq(0) :> void @ b;
		width = (unsigned short)(b - a);
		c <: width;
	}
}
One issue with this approach is that the width of the pulse you can measure is limited by the time it takes to unpause from the first conditional input in the loop, get the timestamp from the port and then setup the next conditional input. If executing these instructions takes too long then the timestamp for the second conditional input will be after the time the pins first went low and so the measured pulse length will be longer than the actual pulse length.

One way to cope with this is to make the port a 32 bit buffered port:

Code: Select all

#include <xs1.h>
#include <xclib.h>

void detector(buffered in port:32 p, streaming chanend c)
{
	unsigned a, b, x, width;
	p when pinseq(0) :> void;
	while (1) {
		p when pinseq(1) :> void @ a;
		p when pinseq(0) :> x @ b;
		width = (unsigned short)(b - a);
		width -= clz(x) - 1;
		c <: width;
	}
}
A conditional input on a buffered port returns the data which met the condition in the most significant port width bits. The rest of the value contains the previous ((transfer width / port width) - 1) values sampled from the pins. Consider the following line:

Code: Select all

p when pinseq(0) :> x @ b;
If the conditional input fired exactly at the transition from 1 to 0 then we get 0 in the most significant bit and 1 in the 2nd most significant bit. On the other hand if the conditional input missed the transition we would expect multiple 0's in the most significant bits. So long as we didn't miss the transition by more than 31 clock ticks the number of clock ticks we missed the transition by can be determined by counting number of extra leading zeros we have after the first most significant zero. We can do this using the clz function. We can then use this value to correct the pulse width calculated using the timestamp values alone. This allows us to accurately measure the pulse width, even if the conditional input fires up to 31 clock ticks after the transition.