2016-05-25 18:37:40 +03:00
|
|
|
// Basic scheduling functions and startup/shutdown code.
|
|
|
|
//
|
2017-05-26 16:14:26 +03:00
|
|
|
// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
|
2016-05-25 18:37:40 +03:00
|
|
|
//
|
|
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
|
|
|
|
#include <setjmp.h> // setjmp
|
|
|
|
#include "autoconf.h" // CONFIG_*
|
2017-08-07 19:33:08 +03:00
|
|
|
#include "basecmd.h" // stats_update
|
2017-07-27 19:05:29 +03:00
|
|
|
#include "board/io.h" // readb
|
2016-05-25 18:37:40 +03:00
|
|
|
#include "board/irq.h" // irq_save
|
2016-06-05 21:58:12 +03:00
|
|
|
#include "board/misc.h" // timer_from_us
|
2017-05-26 15:34:31 +03:00
|
|
|
#include "board/pgm.h" // READP
|
2016-05-25 18:37:40 +03:00
|
|
|
#include "command.h" // shutdown
|
2017-03-25 06:01:08 +03:00
|
|
|
#include "sched.h" // sched_check_periodic
|
2016-05-25 18:37:40 +03:00
|
|
|
#include "stepper.h" // stepper_event
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
|
|
|
* Timers
|
|
|
|
****************************************************************/
|
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
static struct timer periodic_timer, *timer_list = &periodic_timer;
|
2017-07-27 19:19:09 +03:00
|
|
|
static struct timer sentinel_timer, deleted_timer;
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
// The periodic_timer simplifies the timer code by ensuring there is
|
|
|
|
// always a timer on the timer list and that there is always a timer
|
2017-08-07 02:21:16 +03:00
|
|
|
// not far in the future.
|
2016-06-09 04:33:50 +03:00
|
|
|
static uint_fast8_t
|
2017-07-27 19:05:29 +03:00
|
|
|
periodic_event(struct timer *t)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-08-07 18:33:31 +03:00
|
|
|
// Make sure the stats task runs periodically
|
|
|
|
sched_wake_tasks();
|
|
|
|
// Reschedule timer
|
2017-08-07 02:21:16 +03:00
|
|
|
periodic_timer.waketime += timer_from_us(100000);
|
2017-07-27 19:05:29 +03:00
|
|
|
sentinel_timer.waketime = periodic_timer.waketime + 0x80000000;
|
2016-05-25 18:37:40 +03:00
|
|
|
return SF_RESCHEDULE;
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
static struct timer periodic_timer = {
|
|
|
|
.func = periodic_event,
|
2017-03-10 23:11:59 +03:00
|
|
|
.next = &sentinel_timer,
|
|
|
|
};
|
|
|
|
|
|
|
|
// The sentinel timer is always the last timer on timer_list - its
|
|
|
|
// presence allows the code to avoid checking for NULL while
|
|
|
|
// traversing timer_list. Since sentinel_timer.waketime is always
|
2017-07-27 19:05:29 +03:00
|
|
|
// equal to (periodic_timer.waketime + 0x80000000) any added timer
|
|
|
|
// must always have a waketime less than one of these two timers.
|
2017-03-10 23:11:59 +03:00
|
|
|
static uint_fast8_t
|
|
|
|
sentinel_event(struct timer *t)
|
|
|
|
{
|
|
|
|
shutdown("sentinel timer called");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct timer sentinel_timer = {
|
|
|
|
.func = sentinel_event,
|
|
|
|
.waketime = 0x80000000,
|
2016-05-25 18:37:40 +03:00
|
|
|
};
|
|
|
|
|
2017-03-27 22:05:01 +03:00
|
|
|
// Find position for a timer in timer_list and insert it
|
|
|
|
static void __always_inline
|
|
|
|
insert_timer(struct timer *t, uint32_t waketime)
|
|
|
|
{
|
|
|
|
struct timer *prev, *pos = timer_list;
|
|
|
|
for (;;) {
|
|
|
|
prev = pos;
|
|
|
|
if (CONFIG_MACH_AVR)
|
|
|
|
// micro optimization for AVR - reduces register pressure
|
|
|
|
asm("" : "+r"(prev));
|
|
|
|
pos = pos->next;
|
|
|
|
if (timer_is_before(waketime, pos->waketime))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
t->next = pos;
|
|
|
|
prev->next = t;
|
|
|
|
}
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
// Schedule a function call at a supplied time.
|
|
|
|
void
|
2017-03-11 06:12:05 +03:00
|
|
|
sched_add_timer(struct timer *add)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2016-06-07 22:30:24 +03:00
|
|
|
uint32_t waketime = add->waketime;
|
2016-06-09 04:13:53 +03:00
|
|
|
irqstatus_t flag = irq_save();
|
2017-07-27 19:19:09 +03:00
|
|
|
if (unlikely(timer_is_before(waketime, timer_list->waketime))) {
|
|
|
|
// This timer is before all other scheduled timers
|
|
|
|
struct timer *tl = timer_list;
|
|
|
|
if (timer_is_before(waketime, timer_read_time() + timer_from_us(2000)))
|
|
|
|
try_shutdown("Timer too close");
|
|
|
|
if (tl == &deleted_timer)
|
|
|
|
add->next = deleted_timer.next;
|
|
|
|
else
|
|
|
|
add->next = tl;
|
|
|
|
deleted_timer.waketime = waketime;
|
|
|
|
deleted_timer.next = add;
|
|
|
|
timer_list = &deleted_timer;
|
|
|
|
timer_kick();
|
|
|
|
} else {
|
|
|
|
insert_timer(add, waketime);
|
|
|
|
}
|
2016-05-25 18:37:40 +03:00
|
|
|
irq_restore(flag);
|
|
|
|
}
|
|
|
|
|
2017-03-11 04:03:56 +03:00
|
|
|
// The deleted timer is used when deleting an active timer.
|
|
|
|
static uint_fast8_t
|
|
|
|
deleted_event(struct timer *t)
|
|
|
|
{
|
|
|
|
return SF_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct timer deleted_timer = {
|
|
|
|
.func = deleted_event,
|
|
|
|
};
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
// Remove a timer that may be live.
|
|
|
|
void
|
|
|
|
sched_del_timer(struct timer *del)
|
|
|
|
{
|
2016-06-09 04:13:53 +03:00
|
|
|
irqstatus_t flag = irq_save();
|
2016-05-25 18:37:40 +03:00
|
|
|
if (timer_list == del) {
|
2017-03-11 04:03:56 +03:00
|
|
|
// Deleting the next active timer - replace with deleted_timer
|
|
|
|
deleted_timer.waketime = del->waketime;
|
|
|
|
deleted_timer.next = del->next;
|
|
|
|
timer_list = &deleted_timer;
|
2016-06-07 22:30:24 +03:00
|
|
|
} else {
|
|
|
|
// Find and remove from timer list (if present)
|
|
|
|
struct timer *pos;
|
|
|
|
for (pos = timer_list; pos->next; pos = pos->next) {
|
|
|
|
if (pos->next == del) {
|
|
|
|
pos->next = del->next;
|
|
|
|
break;
|
|
|
|
}
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
irq_restore(flag);
|
|
|
|
}
|
|
|
|
|
2017-03-27 23:38:01 +03:00
|
|
|
// Invoke the next timer - called from board hardware irq code.
|
|
|
|
unsigned int
|
|
|
|
sched_timer_dispatch(void)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-27 23:38:01 +03:00
|
|
|
// Invoke timer callback
|
|
|
|
struct timer *t = timer_list;
|
|
|
|
uint_fast8_t res;
|
|
|
|
uint32_t updated_waketime;
|
|
|
|
if (CONFIG_INLINE_STEPPER_HACK && likely(!t->func)) {
|
|
|
|
res = stepper_event(t);
|
|
|
|
updated_waketime = t->waketime;
|
|
|
|
} else {
|
|
|
|
res = t->func(t);
|
|
|
|
updated_waketime = t->waketime;
|
|
|
|
}
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-03-27 23:38:01 +03:00
|
|
|
// Update timer_list (rescheduling current timer if necessary)
|
|
|
|
unsigned int next_waketime = updated_waketime;
|
|
|
|
if (unlikely(res == SF_DONE)) {
|
|
|
|
next_waketime = t->next->waketime;
|
|
|
|
timer_list = t->next;
|
|
|
|
} else if (!timer_is_before(updated_waketime, t->next->waketime)) {
|
|
|
|
next_waketime = t->next->waketime;
|
|
|
|
timer_list = t->next;
|
|
|
|
insert_timer(t, updated_waketime);
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
2017-03-27 23:38:01 +03:00
|
|
|
|
|
|
|
return next_waketime;
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
2017-09-04 05:02:48 +03:00
|
|
|
// Remove all user timers
|
|
|
|
void
|
2017-08-07 02:21:16 +03:00
|
|
|
sched_timer_reset(void)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-11 04:03:56 +03:00
|
|
|
timer_list = &deleted_timer;
|
2017-07-27 19:05:29 +03:00
|
|
|
deleted_timer.waketime = periodic_timer.waketime;
|
|
|
|
deleted_timer.next = &periodic_timer;
|
|
|
|
periodic_timer.next = &sentinel_timer;
|
2017-07-27 19:19:09 +03:00
|
|
|
timer_kick();
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
/****************************************************************
|
2017-08-07 19:33:08 +03:00
|
|
|
* Tasks
|
2017-07-27 19:05:29 +03:00
|
|
|
****************************************************************/
|
|
|
|
|
2017-08-08 22:21:33 +03:00
|
|
|
static int_fast8_t tasks_status;
|
|
|
|
|
|
|
|
#define TS_IDLE -1
|
|
|
|
#define TS_REQUESTED 0
|
|
|
|
#define TS_RUNNING 1
|
2017-08-07 19:33:08 +03:00
|
|
|
|
2017-08-07 18:33:31 +03:00
|
|
|
// Note that at least one task is ready to run
|
|
|
|
void
|
|
|
|
sched_wake_tasks(void)
|
|
|
|
{
|
2017-08-08 22:21:33 +03:00
|
|
|
tasks_status = TS_REQUESTED;
|
2017-08-07 19:33:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if tasks need to be run
|
|
|
|
uint8_t
|
|
|
|
sched_tasks_busy(void)
|
|
|
|
{
|
2017-08-08 22:21:33 +03:00
|
|
|
return tasks_status >= TS_REQUESTED;
|
2017-08-07 18:33:31 +03:00
|
|
|
}
|
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
// Note that a task is ready to run
|
|
|
|
void
|
|
|
|
sched_wake_task(struct task_wake *w)
|
|
|
|
{
|
2017-08-07 18:33:31 +03:00
|
|
|
sched_wake_tasks();
|
2017-07-27 19:05:29 +03:00
|
|
|
writeb(&w->wake, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if a task is ready to run (as indicated by sched_wake_task)
|
|
|
|
uint8_t
|
|
|
|
sched_check_wake(struct task_wake *w)
|
|
|
|
{
|
|
|
|
if (!readb(&w->wake))
|
|
|
|
return 0;
|
|
|
|
writeb(&w->wake, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-08-07 19:33:08 +03:00
|
|
|
// Main task dispatch loop
|
|
|
|
static void
|
|
|
|
run_tasks(void)
|
|
|
|
{
|
|
|
|
uint32_t start = timer_read_time();
|
|
|
|
for (;;) {
|
|
|
|
// Check if can sleep
|
2017-08-08 22:21:33 +03:00
|
|
|
if (tasks_status != TS_REQUESTED) {
|
|
|
|
start -= timer_read_time();
|
|
|
|
irq_disable();
|
|
|
|
if (tasks_status != TS_REQUESTED) {
|
|
|
|
// Sleep processor (only run timers) until tasks woken
|
|
|
|
tasks_status = TS_IDLE;
|
|
|
|
do {
|
|
|
|
irq_wait();
|
|
|
|
} while (tasks_status != TS_REQUESTED);
|
|
|
|
}
|
2017-08-07 19:33:08 +03:00
|
|
|
irq_enable();
|
2017-08-08 22:21:33 +03:00
|
|
|
start += timer_read_time();
|
2017-08-07 19:33:08 +03:00
|
|
|
}
|
2017-08-08 22:21:33 +03:00
|
|
|
tasks_status = TS_RUNNING;
|
2017-08-07 19:33:08 +03:00
|
|
|
|
|
|
|
// Run all tasks
|
|
|
|
extern void ctr_run_taskfuncs(void);
|
|
|
|
ctr_run_taskfuncs();
|
|
|
|
|
|
|
|
// Update statistics
|
|
|
|
uint32_t cur = timer_read_time();
|
|
|
|
stats_update(start, cur);
|
|
|
|
start = cur;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-27 19:05:29 +03:00
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
/****************************************************************
|
|
|
|
* Shutdown processing
|
|
|
|
****************************************************************/
|
|
|
|
|
2017-05-26 15:34:31 +03:00
|
|
|
static uint_fast8_t shutdown_status, shutdown_reason;
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
// Return true if the machine is in an emergency stop state
|
|
|
|
uint8_t
|
|
|
|
sched_is_shutdown(void)
|
|
|
|
{
|
|
|
|
return !!shutdown_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transition out of shutdown state
|
|
|
|
void
|
|
|
|
sched_clear_shutdown(void)
|
|
|
|
{
|
|
|
|
if (!shutdown_status)
|
|
|
|
shutdown("Shutdown cleared when not shutdown");
|
|
|
|
if (shutdown_status == 2)
|
|
|
|
// Ignore attempt to clear shutdown if still processing shutdown
|
|
|
|
return;
|
|
|
|
shutdown_status = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke all shutdown functions (as declared by DECL_SHUTDOWN)
|
|
|
|
static void
|
2017-07-02 21:29:16 +03:00
|
|
|
run_shutdown(int reason)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-03-25 06:01:08 +03:00
|
|
|
uint32_t cur = timer_read_time();
|
2017-07-02 21:29:16 +03:00
|
|
|
if (!shutdown_status)
|
|
|
|
shutdown_reason = reason;
|
2016-05-25 18:37:40 +03:00
|
|
|
shutdown_status = 2;
|
2017-08-07 02:21:16 +03:00
|
|
|
sched_timer_reset();
|
2017-05-26 16:14:26 +03:00
|
|
|
extern void ctr_run_shutdownfuncs(void);
|
|
|
|
ctr_run_shutdownfuncs();
|
2016-05-25 18:37:40 +03:00
|
|
|
shutdown_status = 1;
|
|
|
|
irq_enable();
|
|
|
|
|
2017-03-24 22:05:52 +03:00
|
|
|
sendf("shutdown clock=%u static_string_id=%hu", cur, shutdown_reason);
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
2017-05-26 15:34:31 +03:00
|
|
|
// Report the last shutdown reason code
|
|
|
|
void
|
|
|
|
sched_report_shutdown(void)
|
|
|
|
{
|
|
|
|
sendf("is_shutdown static_string_id=%hu", shutdown_reason);
|
|
|
|
}
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
// Shutdown the machine if not already in the process of shutting down
|
|
|
|
void
|
2017-05-26 15:34:31 +03:00
|
|
|
sched_try_shutdown(uint_fast8_t reason)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
2017-10-12 05:01:45 +03:00
|
|
|
if (!shutdown_status)
|
2016-05-25 18:37:40 +03:00
|
|
|
sched_shutdown(reason);
|
|
|
|
}
|
|
|
|
|
|
|
|
static jmp_buf shutdown_jmp;
|
|
|
|
|
|
|
|
// Force the machine to immediately run the shutdown handlers
|
|
|
|
void
|
2017-05-26 15:34:31 +03:00
|
|
|
sched_shutdown(uint_fast8_t reason)
|
2016-05-25 18:37:40 +03:00
|
|
|
{
|
|
|
|
irq_disable();
|
2017-07-02 21:29:16 +03:00
|
|
|
longjmp(shutdown_jmp, reason);
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
2017-08-07 19:33:08 +03:00
|
|
|
* Startup
|
2016-05-25 18:37:40 +03:00
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
// Main loop of program
|
|
|
|
void
|
|
|
|
sched_main(void)
|
|
|
|
{
|
2017-08-07 19:33:08 +03:00
|
|
|
extern void ctr_run_initfuncs(void);
|
2017-05-26 16:14:26 +03:00
|
|
|
ctr_run_initfuncs();
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-08-08 06:51:25 +03:00
|
|
|
sendf("starting");
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
int ret = setjmp(shutdown_jmp);
|
|
|
|
if (ret)
|
2017-07-02 21:29:16 +03:00
|
|
|
run_shutdown(ret);
|
2016-05-25 18:37:40 +03:00
|
|
|
|
2017-08-07 19:33:08 +03:00
|
|
|
run_tasks();
|
2016-05-25 18:37:40 +03:00
|
|
|
}
|