Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

qapi_Timer_Sleep() does not work with other units than TICK #2

Open
ziron4 opened this issue Nov 6, 2018 · 20 comments
Open

qapi_Timer_Sleep() does not work with other units than TICK #2

ziron4 opened this issue Nov 6, 2018 · 20 comments
Assignees
Labels
bug Something isn't working

Comments

@ziron4
Copy link

ziron4 commented Nov 6, 2018

First of all, thank you for your amazing work!

The issue:
When I try to use the qapi_Timer_Sleep function from the qapi_timer.h, the module never comes back if I use other time units than QAPI_TIMER_UNIT_TICK.

Here is my code (That does not work unless I use the QAPI_TIMER_UNIT_TICK):

#include "qapi.h"
#include "qapi_timer.h"
#include "app_uart.h"

int dam_app_start(void)
{
  uart_dbg_init(); /* Uart setup. */
  
  qapi_Timer_Sleep(1, QAPI_TIMER_UNIT_USEC, true);
  UART_DBG("Module running\n\r");
  return 0;
}
@Thalhammer
Copy link
Owner

Thalhammer commented Nov 6, 2018

I did encounter this problem as well and will look into it, once I'm back home.
Did you try to set deferrable (the last one) to false?
If it is a limitation of the API, one could only try to emulate it using a timer and semaphore, but this might not work for delays so short.

PS: There is now a debug/trace header in utils that allows for printf like traces but prepends the module (and one i figured it out the time).
EDIT: Note that this repo is very much in progress and nowhere near being done yet, so take all information/code with a grain of salt. Also feel free to add new examples and fix bugs you find. PR's are always welcome.

@Thalhammer Thalhammer self-assigned this Nov 6, 2018
@Thalhammer Thalhammer added bug Something isn't working on hold Issue is on hold labels Nov 6, 2018
@Thalhammer
Copy link
Owner

Do you use GCC or ARM ?

@ziron4
Copy link
Author

ziron4 commented Nov 6, 2018

Will try to change the last argument to false.
I have used the qapi_Timer_Sleep function before with no problems

I use GCC

@ziron4
Copy link
Author

ziron4 commented Nov 6, 2018

How did you reverse engineer the .lib files?

@Thalhammer
Copy link
Owner

Thalhammer commented Nov 6, 2018

I used Cutter which is a gui for radare2.
You unpack the lib using arm-ar into individual object files and then you can open them right in Cutter.
Maybe I got something wrong there.
What I found out so far:
Sleep with UNIT_TICK works, but returns immediately even if you specify a long time.
Everything else seems to crash/never return.

EDIT: One thing to note is that unlike all other functions, the sleep api seems to swap parameter positions 1. gets second and 2. gets first. But I tried it the other way and it doesn't work either.

@Thalhammer
Copy link
Owner

Thalhammer commented Nov 6, 2018

It seems like UNIT_TICK is working, a tick is just extremly short.

TRACE("sleep\r\n");
int res = qapi_Timer_Sleep(20000000, QAPI_TIMER_UNIT_TICK, false);
TRACE("res=%d\r\n", res);

Resulted in a delay of about 1s between both prints. I tried smaller values and it seems to scale and be constant.
Calculating this back, a tick seems to be around 0,05 microseconds.
So if we don't get the other units to work we could at least integrate a workaround into qapi_Timer_Sleep that sleeps the requested time using ticks.

EDIT: if the last param is true it does not return.

@ziron4
Copy link
Author

ziron4 commented Nov 6, 2018

It could be a solution, but it gives a max delay of 3.58 min, which is not that long.
I will try to use the qapi_Timer_set and qapi_Timer_stop functions to get a bigger delay.

Sleep with UNIT_USEC seems to run over and return, I know because I waited for over an hour and it returned. uint32_t max = 4294967295 and with 1000000 usec every sec gives a overrun time of 71.6min.

@Thalhammer
Copy link
Owner

It could be a solution, but it gives a max delay of 3.58 min, which is not that long.

If the delay would be larger than say 2 minutes one could just divide by 2 minutes and way multiple times.
The overhead of looping and multiple calls won't be measurable at those delays.

@Thalhammer Thalhammer removed the on hold Issue is on hold label Nov 6, 2018
Thalhammer added a commit that referenced this issue Nov 6, 2018
@Thalhammer
Copy link
Owner

Note that the 19200081 is a guess based on output, but I think it is good enough since you won't be able to do really accurate timing anyway.

@ziron4
Copy link
Author

ziron4 commented Nov 6, 2018

I agree, it should be good enough.
EDIT: It's not realtime anyway

@Thalhammer
Copy link
Owner

However I still think there might be a bug in my code, since I have an app that uses the original lib together with armcc and there is qapi_Timer_Sleep(2,QAPI_TIMER_UNIT_MSEC,true); after the UART transmit and it seems to work fine (I never tested if it really waits 2ms, but it doesn't hang and stop). Note however that this is inside a second thread and not in main. So maybe there is something special with the main thread in regard to timer sleep?
I'll definitely have to look into this more in the future.

@ziron4
Copy link
Author

ziron4 commented Nov 6, 2018

I just tested by creating a second thread and setting it to sleep there, it does not make a differens.
The start and stop timer functions does not seem to work either, it calls the callback function ones and never returns, just like the Timer_Sleep function

@Thalhammer
Copy link
Owner

Thalhammer commented Nov 6, 2018

I wrote a small example for timers and it seems to me the problem is not with stop.
https://gist.github.com/Thalhammer/34f9dd5785d1ad05fc5cbf533a41eaab

It looks like the timing of qapi_Timer_Sleep changes once you start a timer 😒
The timer did get stopped but the sleep call before it took way longer than it should (103 instead of 10 seconds) and the second one never returned.

However the call to qapi_Timer_Stop worked fine and the callback was no more called after it.

@ziron4
Copy link
Author

ziron4 commented Nov 8, 2018

I have done som digging,
ThreadX has some timer functions which we can use:
The tx_thread_sleep(n) function works by suspending the thread for n "timer ticks" which is default 100 ticks pr second (it can be user defined by defining TX_TIMER_TICKS_PER_SECOND in tx_port.h). That means every "timer ticks" is approx 10ms long.

Threadx_User_guide.pdf

@Thalhammer
Copy link
Owner

tx_thread_sleep seems to work (at least it did in all cases I used it so far) but I'm not sure if TX_TIMER_TICKS_PER_SECOND is really 100 in this case. I used it in example 03-gpio and it seemed more like its defined to a value in the neighbourhood of 60.

@Thalhammer
Copy link
Owner

Thalhammer commented Nov 12, 2018

So after my first attempt to calculate a valid time using UNIT_TICK did not work and all units seem to somehow depend on the system state I decided to emulate them using a timer, which as far as I know seems to work fine. This, however, comes with some implications:

  • USEC delay is out of the question
  • MSEC delay works if the delay is long enough

You can check it out here: d631fcb

Thus requesting UNIT_USEC or UNIT_MSEC with a timeout of less than 10 will just return ERR_NOT_SUPPORTED.
UNIT_TICK is still implemented using the original API since I have no idea how long a tick should be in the first place (maybe tx_thread_sleep would be an option here ?).
I thought about calibrating a delay loop based on a timer and using it to implement usec and small msec delays, but depending on the hardware (variable CPU frequency ?) this might not work and it would require an additional init function which is not part of the original API.
Do you have an idea on how to do this?
Do you even use such small delays, because I can't really figure out a use case for them?

@ziron4
Copy link
Author

ziron4 commented Nov 12, 2018

The reason why a delay smaller than 10 is not supported is because the OS simply cannot by definition create a delay smaller than the tick smaller than the tick size (because it's software timers.).
The only right implementation of USEC delays is with a hardware timer.

I do not think it's your implementation of the libs.

The problem must be that the hardware timer has not been init.

@Thalhammer
Copy link
Owner

Thalhammer commented Nov 12, 2018

Na I don't think so, In fact, I assume qapi_Timer is backed by a hardware timer of some sort because the real limit was about 5ms, but the problem is that using a timer + event flags means that there will be calls to the scheduler which makes anything below a certain time helplessly inaccurate.

The problem must be that the hardware timer has not been init.

If there is one, I don't have the slightest idea how to do so. Even if a timer is running sleeps based on tick are totally unpredictable. The same program that had a sleep time of 103 seconds this week took only 98s, so maybe it depends on how the stars are aligned?

Everything with more than 10ms is now backed by a timer and is quite accurate (+/- ~2ms).

@Thalhammer
Copy link
Owner

I'm not sure anymore if my implementation is really correct because I have a simple gpio example that works fine with armcc but not with gcc.

@Thalhammer
Copy link
Owner

Seems like this is not the end of the story:
Because of #4, the workaround implemented ceases to work after a few executions of sleep.
I therefore strongly recommend against using qapi_Timer_Sleep in anyway.

Thalhammer added a commit that referenced this issue Mar 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants