nanospin

We are calling nanospin() function with count value for 10usec in interrupt handler for int 0(tick interrupt). we used InterruptDisable(), InterruptEnable functions before we call nanospin. we called nanospin_calibrate() once before attaching inetrrupt handler.
Used port80 debugging for checking the accuracy of nanospin. But found that, the spin count is never the same, every tick.
There is a variation of maximum 2usec.

We need timings to be accurate for our application.
We used assembly instructions to generate this in QNX4. But for portability reasons we deviated from assembly. In QNX4, we did not see this amount of variation.

Is there a way we can get around this, Or assembly is the best solution?

Request your inputs,
Thanks,
radha

What is your processor model?

What do you mean by the spin count is never the same?

P1 processor. we are not able to get the delay from nanospin(count_value). The delay is varying when measured using logic analyzer around nanospin.

I report this, because it can easily be overlooked. Did you mean an Intel P1? I don’t know of an Intel P1 processor that had a “supervisor” mode, but there were integrated P1 type processors that did. What is the point? A processor with supervisor mode turned on is inherently non-real-time. Even with interrupts turned off, the supervisor mode can take over and leave you wondering where the cycles went.

yes it is Intel P1 processor. But with QNX4, we used assembly instructions to get the delay. We also used sti, cli instructions before and after. But the variations was not so much.
With QNX6, for portability, we used QNX6 API for delay. Not aware of this supervisory mode.
Both software are running on same processor card. But could see the variations more in QNX6 comaparatively.

Any suggestions?

Thanks,
radha

Could you provide source code? I would like to see if you call nanospin() from ISR or interrupt handling thread. Also, it’s interesting to see how exactly you measure. 2 us sounds a way too much, looks like you have some context swithing involved.

FWIW here is the source to nanospin_count() - it’s pretty simple…

void
nanospin_count(unsigned long count) {
    /* Use a volatile counter to scare the compilers */
    volatile unsigned long c = count;

    while(c) {
        --c;
    }
}

This is the code for nanospin_count ( which is the function that does the actual spinning)

0x80491e4 <nanospin_count>: push %ebp
0x80491e5 <nanospin_count+1>: mov %esp,%ebp
0x80491e7 <nanospin_count+3>: sub $0x18,%esp
0x80491ea <nanospin_count+6>: mov 0x8(%ebp),%eax
0x80491ed <nanospin_count+9>: jmp 0x80491f4 <nanospin_count+16>
0x80491ef <nanospin_count+11>: nop
0x80491f0 <nanospin_count+12>: mov 0xfffffffc(%ebp),%eax
0x80491f3 <nanospin_count+15>: dec %eax
0x80491f4 <nanospin_count+16>: mov %eax,0xfffffffc(%ebp)
0x80491f7 <nanospin_count+19>: mov 0xfffffffc(%ebp),%eax
0x80491fa <nanospin_count+22>: test %eax,%eax
0x80491fc <nanospin_count+24>: jne 0x80491f0 <nanospin_count+12>
0x80491fe <nanospin_count+26>: leave
0x80491ff <nanospin_count+26>: ret

As you can see there is no black magic in there. Just a simple loop.

You might try to call nanospin_count instead of nanospin(). You just need to use nanospin_ns_to_count to obtain the value to pass to nanospin_count ( I would do it outside of the interrupt ).

I got down to the assembly code ( via gdb ) because I wanted to see if there wasn’t something in there that would re-enable interrupt as it would have explain the jitter you are seeing.

I didn’t completely follow the code from nanospin() down to nanpspin_count, so there could be something that re-enable interrupt (though I doubt it). If you use nanospin_count though that will rule this possibility out.

Depending on the cache state nano_spin_ns_to_count could have different execution time, it’s a rather long function and it uses a divide instruction. I’m not convinced it could explain the 2us variation thought.

We were trying to measure assembly timings using the code below.

But we are facing some compilation issue with outb calls. it gives the
error as below, for code
asm(" movb $0x80, %dl;
movb $0x01, %al;
outb %al, %dl"
:
:
);
cc -o firectrl2501 firectrl2501.cc
{standard input}: Assembler messages:
{standard input}:33: Error: suffix or operands invalid for `out’
cc: /usr/qnx630/host/qnx6/x86/usr/bin/ntox86-as error 1 Checked some
sites, outb seems to be correct instruction?

Since assembly is found comparatively better, we are planning to build delay loops in assmebly instead of nanospin.

Request your inputs,

Thanks,
radha

I have no experience with assembler on QNX 6, but something jumps out immediately.
outb al,dx → output a byte from the 8 bit al register to an 8 bit port
outw ax,dx → output a two byte value from the 16 bit ax register to a 16 bit port
outb al,dl → this is not part of x86 asm.

In all cases, the port number is put in the DX register, not the DL register.

outp instruction often tends to make the time of assembly code more “stable”. The reason is that quite often port 0x80 in this example is assume to be on the ISA bus, hence the CPU will have to slow down to the speed of the ISA bus while performing that instruction. That makes the instruction incredibly long compare to other instruction and help reduce effect of cache.

Look at x86/inout.h

You should use the out8() inline