4
\$\begingroup\$

I have a Qsys system which includes a NIOS II/e CPU (which is the only master on the bus) and a 36kB chunk of 32-bit internal RAM. The RAM is configured to have the minimum read latency (which is 1 cycle). The whole system is clocked by a single 50MHz clock.

I'm trying to use the usleep function to wait for 1 second and use clock to measure the time:

#include <sys/alt_stdio.h>
#include <unistd.h>
#include <time.h>

void main(void) {
    clock_t start, end;
    start = clock();
    usleep(1000000);
    end = clock();
    alt_printf("usleep(1s) takes 0x%xms\n", (end - start) * 1000 / CLOCKS_PER_SEC);
}

Surprisingly, I get a value 0x592 (1426 DEC), which is not even close to 1 second. Toggling a pin before and after usleep and measuring the time externally confirms that the delay is indeed close to 1.4s.

I understand that busy-waiting is affected by RAM latency and bus arbitration, which I carefully tried to exclude. What is the expected system configuration on which usleep works correctly? Is there an alternative delay function with μs resolution which would work on the configuration I have?

To answer the clarification request, all durations are wrong by about the same factor (1.4), which I suppose depends on the hardware configuration. Simply multiplying by the corrective factor is not a solution for me, since this is for a project which should allow the users to run Arduino code on any FPGA capable of implementing NIOS.

The usleep implementation I'm using comes from Altera "Small C library", AFAIK it's a clone of newlib. Compiler options from "Hello World Small" sample project reproduce the issue. I tried several combinations of options, and none of them fixed the problem. Again, since this is for a library, I'd like to find a solution which works with different (reasonable) compiler options.

\$\endgroup\$
  • \$\begingroup\$ hmmm try nanosleep(1000000000); \$\endgroup\$ – Tony Stewart Sunnyskyguy EE75 Jan 26 '18 at 22:39
  • \$\begingroup\$ @TonyStewart.EEsince'75 You mean nanosleep(struct timespec{1,0})? Sorry, I don't have it. I'm limited to newlib and Altera HAL. \$\endgroup\$ – Dmitry Grigoryev Jan 26 '18 at 22:55
  • \$\begingroup\$ What happens if you wait for smaller time periods (eg. 100ms, 10ms)? \$\endgroup\$ – Bruce Abbott Jan 26 '18 at 22:59
  • \$\begingroup\$ @BruceAbbott Pretty much the same: e.g. usleep(1000). \$\endgroup\$ – Dmitry Grigoryev Jan 26 '18 at 23:12
  • 2
    \$\begingroup\$ I'm voting to close this question as off-topic because it has sat without clarification to an answerable state for nearly six months. Explain if you are trying to figure out why the clock test reads short or the scope test long. Explain how their error tracks for other durations. Explain which of the NIOS software stack options is in use and where the implementation of usleep() comes from. If possible, add debugging within the implementation. Also show clock values periodically from boot. \$\endgroup\$ – Chris Stratton Jun 17 '18 at 22:54
1
\$\begingroup\$

If this is the whole code and not a pseudo code trimmed for this post, it is clear that you have a problem with newlib implementation.

The quick solution is making your own macro using clock()

end = clock() + delay;
while((end - clock()) > 0);
| improve this answer | |
\$\endgroup\$
  • 2
    \$\begingroup\$ It's a bit early to be jumping to that conclusion. For starters, the idea that it's using newlib is presumably one you are only inferring from experience of what you have usually seen on NIOS projects. For all we know, the actual implementation may have usleep() provided by a an (RT)OS kernel. And it's a little odd to blame the library immediately when it is surrounded by custom computing machinery and hardware drivers which have not been proven to be correctly configured for the details of that. \$\endgroup\$ – Chris Stratton Jun 17 '18 at 22:55
  • \$\begingroup\$ The whole code is rather large to be posted or asked about, so I posted a complete example which reproduces the problem. I think I'll give your solution a shot. The only downside I see is that it won't produce very short delays reliably, I'll have to check if this is a problem. \$\endgroup\$ – Dmitry Grigoryev Jun 18 '18 at 7:25
2
\$\begingroup\$

I had the same problem and got the same result. For 1 second I had about real 2 s pause. The reason is that it is realized by software cycle, I guess. At least, here is the comment from Intel FPGA (prev. Altera) https://www.intel.com/content/www/us/en/programmable/support/support-resources/knowledge-base/solutions/rd08312011_772.html

You can resolve your need of good timing count by using ticks counting something like that:

#include <sys/alt_alarm.h>
...
//somewhere in your program
void function_t(){
int prev= 0;
int now;
now = (int) (alt_nticks() * alt_ticks_per_second() / 1000);
if ((now - prev) < period)
return;
last = now;
//my action
}

so I used such function in my round-robin code to do something with using timer. But if you are really want to use timer precisely, you should use interrupts.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I ended up implementing millis() using the timer and micros() using the busy-wait loop requiring manual calibration. Having 1ns system timer is not practically possible with NIOS HAL, since that interrupt will prevent anything else from executing. \$\endgroup\$ – Dmitry Grigoryev Sep 21 '18 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.