Thalhammer / simcom_dam

Examples for simcoms downloadable modules (Qualcomm SoC)
27 stars 13 forks source link

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

Open ziron4 opened 5 years ago

ziron4 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

Do you use GCC or ARM ?

ziron4 commented 5 years ago

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 commented 5 years ago

How did you reverse engineer the .lib files?

Thalhammer commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

Thalhammer commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 :unamused: 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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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:

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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.