Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions src/ar_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ typedef struct _ar_kernel {
uint32_t version; //!< Argon version in BCD, same as #AR_VERSION.
ar_deferred_action_queue_t deferredActions; //!< Actions deferred from interrupt context.
volatile int32_t lockCount; //!< Whether the kernel is locked.
volatile uint32_t tickCount; //!< Current tick count.
int32_t missedTickCount; //!< Number of ticks that occurred while the kernel was locked.
uint32_t nextWakeup; //!< Time of the next wakeup event.
uint32_t threadIdCounter; //!< Counter for generating unique thread IDs.
#if AR_ENABLE_SYSTEM_LOAD
Expand Down Expand Up @@ -175,16 +173,18 @@ extern ar_kernel_t g_ar;
//@{
void ar_port_init_system();
void ar_port_init_tick_timer();
void ar_port_set_timer_delay(bool enable, uint32_t delay_us);
uint32_t ar_port_get_timer_elapsed_us();
void ar_port_set_time_delay(bool enable, uint32_t delay_us);
uint32_t ar_port_get_time_absolute_ticks();
uint32_t ar_port_get_time_absolute_ms();
uint64_t ar_port_get_time_absolute_us();
void ar_port_prepare_stack(ar_thread_t * thread, uint32_t stackSize, void * param);
void ar_port_service_call();
bool ar_port_get_irq_state();
//@}

//! @name Kernel internals
//@{
bool ar_kernel_increment_tick_count(unsigned ticks);
bool ar_kernel_process_ticks();
void ar_kernel_enter_scheduler();
void ar_kernel_run_deferred_actions();
void ar_kernel_scheduler();
Expand Down Expand Up @@ -291,6 +291,29 @@ class KernelGuard
typedef KernelGuard<true> KernelLock; //!< Lock kernel.
typedef KernelGuard<false> KernelUnlock; //!< Unlock kernel.

// when there is no other way and you really have to disable interrupt for short moment in some scope
class IrqGuard
{
private:
// in fact we really need just one bit to store,
uint32_t cpuSR; // occupies 4B on stack but saves 3 masking instruction
// uint8_t cpuSR; // occupies 1B on stack (depends on alignment of surrounding data), adds cca 2 masking instructions
public:
IrqGuard(void)
{
asm ( \
"mrs %[output], PRIMASK\n\t" \
"cpsid I\n\t" \
: [output] "=r" (cpuSR) ::);
}
~IrqGuard(void)
{
asm ( \
"msr PRIMASK, %[input];\n\t" \
::[input] "r" (cpuSR) : );
};
};

#endif // _AR_INTERNAL_H_
//------------------------------------------------------------------------------
// EOF
Expand Down
67 changes: 14 additions & 53 deletions src/ar_kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ static void idle_entry(void * param)
{
while (1)
{
while (g_ar.tickCount < g_ar.nextWakeup)
while (ar_get_tick_count() < g_ar.nextWakeup)
{
}

Expand Down Expand Up @@ -207,22 +207,12 @@ void ar_kernel_periodic_timer_isr()
// as the kernel gets unlocked.
if (g_ar.lockCount)
{
ar_atomic_add32(&g_ar.missedTickCount, 1);
g_ar.flags.needsReschedule = true;
return;
}

#if AR_ENABLE_TICKLESS_IDLE
// Get the elapsed time since the last timer tick.
uint32_t us = ar_port_get_timer_elapsed_us();
uint32_t elapsed_ticks = us / (kSchedulerQuanta_ms * 1000);
#else // AR_ENABLE_TICKLESS_IDLE
uint32_t elapsed_ticks = 1;
#endif // AR_ENABLE_TICKLESS_IDLE

// Process elapsed time. Invoke the scheduler if any threads were woken or if
// round robin scheduling is in effect.
if (ar_kernel_increment_tick_count(elapsed_ticks) || g_ar.flags.needsRoundRobin)
if (ar_kernel_process_ticks() || g_ar.flags.needsRoundRobin)
{
ar_port_service_call();
}
Expand All @@ -243,28 +233,10 @@ uint32_t ar_kernel_yield_isr(uint32_t topOfStack)
g_ar.currentThread->m_stackPointer = reinterpret_cast<uint8_t *>(topOfStack);
}

// Handle any missed ticks.
{
uint32_t missedTicks = g_ar.missedTickCount;
while (!ar_atomic_cas32(&g_ar.missedTickCount, missedTicks, 0))
{
missedTicks = g_ar.missedTickCount;
}

if (missedTicks)
{
ar_kernel_increment_tick_count(missedTicks);
}
}

// Process any deferred actions.
ar_kernel_run_deferred_actions();

#if AR_ENABLE_TICKLESS_IDLE
// Process elapsed time to keep tick count up to date.
uint32_t elapsed_ticks = ar_port_get_timer_elapsed_us() / (kSchedulerQuanta_ms * 1000);
ar_kernel_increment_tick_count(elapsed_ticks);
#endif // AR_ENABLE_TICKLESS_IDLE
ar_kernel_process_ticks();

// Run the scheduler. It will modify g_ar.currentThread if switching threads.
ar_kernel_scheduler();
Expand All @@ -285,15 +257,12 @@ uint32_t ar_kernel_yield_isr(uint32_t topOfStack)
//! and must be at least 1, but may be higher if interrupts are disabled for a
//! long time.
//! @return Flag indicating whether any threads were modified.
bool ar_kernel_increment_tick_count(unsigned ticks)
bool ar_kernel_process_ticks()
{
// assert(ticks > 0);

// Increment tick count.
g_ar.tickCount += ticks;

// Compare against next wakeup time we previously computed.
if (g_ar.tickCount < g_ar.nextWakeup)
if (ar_get_tick_count() < g_ar.nextWakeup)
{
return false;
}
Expand All @@ -309,7 +278,8 @@ bool ar_kernel_increment_tick_count(unsigned ticks)
ar_list_node_t * next = node->m_next;

// Is it time to wake this thread?
if (g_ar.tickCount >= thread->m_wakeupTime)
//TODO: would it be safe to cache tick_count?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that too. It's probably safe to cache it within a single function like this one, or ar_kernel_scheduler().

if (ar_get_tick_count() >= thread->m_wakeupTime)
{
wasThreadWoken = true;

Expand Down Expand Up @@ -524,12 +494,12 @@ void ar_kernel_scheduler()
{
g_ar.nextWakeup = wakeup;
uint32_t delay = 0;
if (g_ar.nextWakeup && g_ar.nextWakeup > g_ar.tickCount)
if (g_ar.nextWakeup && g_ar.nextWakeup > ar_get_tick_count())
{
delay = (g_ar.nextWakeup - g_ar.tickCount) * kSchedulerQuanta_ms * 1000;
delay = (g_ar.nextWakeup - ar_get_tick_count()) * kSchedulerQuanta_ms * 1000;
}
bool enable = (g_ar.nextWakeup != 0);
ar_port_set_timer_delay(enable, delay);
ar_port_set_time_delay(enable, delay);
}
#endif // AR_ENABLE_TICKLESS_IDLE
}
Expand Down Expand Up @@ -572,7 +542,7 @@ uint32_t ar_kernel_get_next_wakeup_time()
if (g_ar.flags.needsRoundRobin)
{
// No need to check sleeping threads!
return g_ar.tickCount + 1;
return ar_get_tick_count() + 1;
}

// Check for a sleeping thread. The sleeping list is sorted by wakeup time, so we only
Expand Down Expand Up @@ -610,23 +580,14 @@ uint32_t ar_get_system_load(void)
// See ar_kernel.h for documentation of this function.
uint32_t ar_get_tick_count(void)
{
#if AR_ENABLE_TICKLESS_IDLE
uint32_t elapsed_ticks = ar_port_get_timer_elapsed_us() / (kSchedulerQuanta_ms * 1000);
return g_ar.tickCount + elapsed_ticks;
#else
return g_ar.tickCount;
#endif // AR_ENABLE_TICKLESS_IDLE
return ar_port_get_time_absolute_ticks();
}

// See ar_kernel.h for documentation of this function.
uint32_t ar_get_millisecond_count(void)
{
#if AR_ENABLE_TICKLESS_IDLE
uint32_t elapsed_ms = ar_port_get_timer_elapsed_us() / 1000;
return g_ar.tickCount * ar_get_milliseconds_per_tick() + elapsed_ms;
#else
return g_ar.tickCount * ar_get_milliseconds_per_tick();
#endif // AR_ENABLE_TICKLESS_IDLE
return ar_port_get_time_absolute_ms();
// return ar_port_get_time_absolute_ticks() * kSchedulerQuanta_ms; //do we have to limit resolution?
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo, it's better if resolution is not limited.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I was not sure about consequences. Will Fix.

Comment on lines 581 to +590
Copy link
Owner

@flit flit Apr 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ar_get_tick_count() and ar_get_millisecond_count() should probably be inline. That does mean exposing some port APIs publicly…

Or can we just completely remove them? Is there any reason to not directly call ar_port_get_time_absolute_<x>()? (Renamed?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, forgot to inline them. Even though, compiler would probably inline them anyway.
Well, I think that application should have access to system time. Also application should not have access to port functions.

}

//! Updates the list node's links and those of @a node so that the object is inserted before
Expand Down
8 changes: 4 additions & 4 deletions src/ar_runloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ ar_status_t ar_runloop_run(ar_runloop_t * runloop, uint32_t timeout, ar_runloop_
}

// Prepare timeout.
uint32_t startTime = g_ar.tickCount;
uint32_t startTime = ar_get_tick_count();
uint32_t timeoutTicks = timeout;
if (timeout != kArInfiniteTimeout)
{
Expand Down Expand Up @@ -197,7 +197,7 @@ ar_status_t ar_runloop_run(ar_runloop_t * runloop, uint32_t timeout, ar_runloop_
uint32_t blockTimeoutTicks = timeoutTicks;
if (blockTimeoutTicks != kArInfiniteTimeout)
{
uint32_t deltaTicks = g_ar.tickCount - startTime;
uint32_t deltaTicks = ar_get_tick_count() - startTime;
if (deltaTicks >= timeoutTicks)
{
// Timed out, exit runloop.
Expand All @@ -213,9 +213,9 @@ ar_status_t ar_runloop_run(ar_runloop_t * runloop, uint32_t timeout, ar_runloop_
ar_timer_t * timer = runloop->m_timers.getHead<ar_timer_t>();

// Timers should always have a wakeup time in the future.
assert (timer->m_wakeupTime >= g_ar.tickCount);
assert (timer->m_wakeupTime >= ar_get_tick_count());

uint32_t wakeupDeltaTicks = timer->m_wakeupTime - g_ar.tickCount;
uint32_t wakeupDeltaTicks = timer->m_wakeupTime - ar_get_tick_count();
// kArInfiniteTimeout is the max 32-bit value, so wakeupDeltaTicks will always be <=
if (wakeupDeltaTicks < blockTimeoutTicks)
{
Expand Down
4 changes: 2 additions & 2 deletions src/ar_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ void ar_thread_sleep(uint32_t milliseconds)
}
else
{
ar_thread_sleep_until(ar_ticks_to_milliseconds(g_ar.tickCount) + milliseconds);
ar_thread_sleep_until(ar_ticks_to_milliseconds(ar_get_tick_count()) + milliseconds);
}
}

Expand Down Expand Up @@ -541,7 +541,7 @@ void _ar_thread::block(ar_list_t & blockedList, uint32_t timeout)
// If a valid timeout was given, put the thread on the sleeping list.
if (timeout != kArInfiniteTimeout)
{
m_wakeupTime = g_ar.tickCount + ar_milliseconds_to_ticks(timeout);
m_wakeupTime = ar_get_tick_count() + ar_milliseconds_to_ticks(timeout);
g_ar.sleepingList.add(this);
}
else
Expand Down
10 changes: 5 additions & 5 deletions src/ar_timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ ar_status_t ar_timer_start(ar_timer_t * timer)
// The callback should have been verified by the create function.
assert(timer->m_callback);

uint32_t wakeupTime = g_ar.tickCount + timer->m_delay;
uint32_t wakeupTime = ar_get_tick_count() + timer->m_delay;

// Handle irq state by deferring the operation.
if (ar_port_get_irq_state())
Expand Down Expand Up @@ -243,7 +243,7 @@ void ar_kernel_run_timers(ar_list_t & timersList)
assert(timer);

// Exit loop if all remaining timers on the list wake up in the future.
if (timer->m_wakeupTime > g_ar.tickCount)
if (timer->m_wakeupTime > ar_get_tick_count())
{
break;
}
Expand All @@ -269,16 +269,16 @@ void ar_kernel_run_timers(ar_list_t & timersList)
// Restart a periodic timer without introducing (much) jitter. Also handle
// the cases where the timer callback ran longer than the next wakeup.
uint32_t wakeupTime = timer->m_wakeupTime + timer->m_delay;
if (wakeupTime == g_ar.tickCount)
if (wakeupTime == ar_get_tick_count())
{
// Push the wakeup out another period into the future.
wakeupTime += timer->m_delay;
}
else if (wakeupTime < g_ar.tickCount)
else if (wakeupTime < ar_get_tick_count())
{
// Compute the delay to the next wakeup in the future that is aligned
// to the timer's period.
uint32_t delta = (g_ar.tickCount - timer->m_wakeupTime + timer->m_delay - 1)
uint32_t delta = (ar_get_tick_count() - timer->m_wakeupTime + timer->m_delay - 1)
/ timer->m_delay * timer->m_delay;
wakeupTime = timer->m_wakeupTime + delta;
}
Expand Down
Loading