BabbleSim triggers interrupts when exiting from critical section (i.e.
interrupts are unlocked) and then we check for pending context switch.
This can lead to invalid switch from isr to task context before isr has
actually finished:
- interrupts are unlocked
- interrupt is triggered
- isr calls os_* which uses critical section and triggers context switch
(e.g. os_eventq_put)
- when exiting critical section in isr, we check for pending context
switch and immediately switch to task context before isr is completed
To fix this we should only perform context switch if exiting from
critical section in task context.
This commit makes LL stack size configurable. Depending on compiler and
platform default value may not be enough. It was reported that on nRF52840
90 words is not enough even if Ext Adv is disabled. To keep things simple
lets just increase default stack size to 120 word for all usecases (with
exception of nRF51 where defults to 96 bytes).
It seems that sometimes EDTT can send a command before cs/cc is consumed
by get_event and this triggers an error since cmd buffer is reused for
cs/cc and thus it will be queued as an event on edtt_q_event.
Not sure if this is an EDTT issue/feature or smth on our side, for now
we can workaround this by simply creating a copy of cs/cc which can be
safely enqueued on edtt_q_event waiting to be consumed and freed by
get_event while original cmd buffer is freed as soon as cs/cc is
processed.
This reworks events handling in EDTT.
The "service_events" routine is now handled in separate task which
blocks until new event is put in queue. This is possible since we use
os_eventq to pass events and this means any get from queue will put
task to sleep properly and trigger context switch if necessary.
The blocking edtt_read() will now sleep if not data is available instead
of advancing time machine forward so it does not interfere with timing.
OS_TICKS_PER_SEC was increased to 1024 to have better granularity and
allow ~5ms sleep time for edtt poller task as required.
Also some non-LL code is simplified to use simple calloc/free as it does
not really seem to be cessary to use mempools there, i.e. we just need
some memory block to pass data and it does not really matter how it's
allocated. Also using calloc/free means we will never run out of memory
so no need to check for that.
This changes tasks handling to native threads instead of setlongjmp()
which resolves issue with calling the setlongjmp() from nested signal
handlers but also simplifies code, makes debugging much easier and can
work nicely with e.g. Valgrind.
Each task is wrapped in a thread and all threads are synchronized on
a global mutex to make sure only one task executes at any time. If
context switch is requested (this is always done via os_sched() in
critical section), a flag is set to indicate pending context switch
which will be handled after exiting from critical setion and handling
all other pending interrupts. This mimics the way it's done on a real
hardware.