irq: Support sleeping when mcu is idle

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-07-12 22:16:16 -04:00
parent 969485c754
commit 118fd21cb8
13 changed files with 101 additions and 10 deletions

View File

@ -420,6 +420,7 @@ class MCU:
self._stats_sumsq_base = 0. self._stats_sumsq_base = 0.
self._mcu_tick_avg = 0. self._mcu_tick_avg = 0.
self._mcu_tick_stddev = 0. self._mcu_tick_stddev = 0.
self._mcu_tick_awake = 0.
def handle_mcu_stats(self, params): def handle_mcu_stats(self, params):
count = params['count'] count = params['count']
tick_sum = params['sum'] tick_sum = params['sum']
@ -427,6 +428,7 @@ class MCU:
self._mcu_tick_avg = tick_sum * c self._mcu_tick_avg = tick_sum * c
tick_sumsq = params['sumsq'] * self._stats_sumsq_base tick_sumsq = params['sumsq'] * self._stats_sumsq_base
self._mcu_tick_stddev = c * math.sqrt(count*tick_sumsq - tick_sum**2) self._mcu_tick_stddev = c * math.sqrt(count*tick_sumsq - tick_sum**2)
self._mcu_tick_awake = tick_sum / self._mcu_freq
def handle_shutdown(self, params): def handle_shutdown(self, params):
if self.is_shutdown: if self.is_shutdown:
return return
@ -493,9 +495,9 @@ class MCU:
self._ffi_lib.steppersync_free(self._steppersync) self._ffi_lib.steppersync_free(self._steppersync)
self._steppersync = None self._steppersync = None
def stats(self, eventtime): def stats(self, eventtime):
return "%s mcu_task_avg=%.06f mcu_task_stddev=%.06f" % ( return "%s mcu_awake=%.03f mcu_task_avg=%.06f mcu_task_stddev=%.06f" % (
self.serial.stats(eventtime), self.serial.stats(eventtime),
self._mcu_tick_avg, self._mcu_tick_stddev) self._mcu_tick_awake, self._mcu_tick_avg, self._mcu_tick_stddev)
def force_shutdown(self): def force_shutdown(self):
self.send(self._emergency_stop_cmd.encode()) self.send(self._emergency_stop_cmd.encode())
def microcontroller_restart(self): def microcontroller_restart(self):

View File

@ -28,6 +28,10 @@ static inline void irq_restore(irqstatus_t flag) {
SREG = flag; SREG = flag;
} }
static inline void irq_wait(void) {
asm("sei\n sleep\n cli" : : : "memory");
}
static inline void irq_poll(void) { static inline void irq_poll(void) {
} }

View File

@ -6,6 +6,7 @@
#include <avr/interrupt.h> // TCNT1 #include <avr/interrupt.h> // TCNT1
#include "autoconf.h" // CONFIG_AVR_CLKPR #include "autoconf.h" // CONFIG_AVR_CLKPR
#include "basecmd.h" // stats_note_sleep
#include "board/misc.h" // timer_from_us #include "board/misc.h" // timer_from_us
#include "command.h" // shutdown #include "command.h" // shutdown
#include "irq.h" // irq_save #include "irq.h" // irq_save
@ -62,6 +63,12 @@ timer_set(uint16_t next)
OCR1A = next; OCR1A = next;
} }
static inline uint16_t
timer_get_next(void)
{
return OCR1A;
}
static inline void static inline void
timer_repeat_set(uint16_t next) timer_repeat_set(uint16_t next)
{ {
@ -103,6 +110,9 @@ timer_init(void)
// enable interrupt // enable interrupt
TIMSK1 = 1<<OCIE1A; TIMSK1 = 1<<OCIE1A;
irq_restore(flag); irq_restore(flag);
// Enable idle on sleep instruction
SMCR = 0x01;
} }
DECL_INIT(timer_init); DECL_INIT(timer_init);
@ -196,8 +206,22 @@ done:
void void
timer_task(void) timer_task(void)
{ {
static uint16_t last_timer;
uint16_t lst = last_timer;
irq_disable(); irq_disable();
timer_repeat_set(timer_get() + TIMER_IDLE_REPEAT_TICKS); uint16_t next = timer_get_next(), cur = timer_get();
if (lst != next) {
timer_repeat_set(cur + TIMER_IDLE_REPEAT_TICKS);
irq_enable();
last_timer = next;
return;
}
// Sleep the processor
irq_wait();
uint16_t post_sleep = timer_get();
timer_repeat_set(post_sleep + TIMER_IDLE_REPEAT_TICKS);
irq_enable(); irq_enable();
stats_note_sleep(post_sleep - cur);
} }
DECL_TASK(timer_task); DECL_TASK(timer_task);

View File

@ -188,6 +188,7 @@ command_get_status(uint32_t *args)
DECL_COMMAND_FLAGS(command_get_status, HF_IN_SHUTDOWN, "get_status"); DECL_COMMAND_FLAGS(command_get_status, HF_IN_SHUTDOWN, "get_status");
static uint32_t stats_send_time, stats_send_time_high; static uint32_t stats_send_time, stats_send_time_high;
static uint32_t stats_last_time, stats_sleep_time;
void void
command_get_uptime(uint32_t *args) command_get_uptime(uint32_t *args)
@ -198,16 +199,23 @@ command_get_uptime(uint32_t *args)
} }
DECL_COMMAND_FLAGS(command_get_uptime, HF_IN_SHUTDOWN, "get_uptime"); DECL_COMMAND_FLAGS(command_get_uptime, HF_IN_SHUTDOWN, "get_uptime");
void
stats_note_sleep(uint32_t sleep_time)
{
stats_sleep_time += sleep_time;
stats_last_time += sleep_time;
}
#define SUMSQ_BASE 256 #define SUMSQ_BASE 256
DECL_CONSTANT(STATS_SUMSQ_BASE, SUMSQ_BASE); DECL_CONSTANT(STATS_SUMSQ_BASE, SUMSQ_BASE);
void void
stats_task(void) stats_task(void)
{ {
static uint32_t last, count, sumsq; static uint32_t count, sumsq;
uint32_t cur = timer_read_time(); uint32_t cur = timer_read_time();
uint32_t diff = cur - last; uint32_t diff = cur - stats_last_time;
last = cur; stats_last_time = cur;
count++; count++;
// Calculate sum of diff^2 - be careful of integer overflow // Calculate sum of diff^2 - be careful of integer overflow
uint32_t nextsumsq; uint32_t nextsumsq;
@ -224,10 +232,12 @@ stats_task(void)
if (timer_is_before(cur, stats_send_time + timer_from_us(5000000))) if (timer_is_before(cur, stats_send_time + timer_from_us(5000000)))
return; return;
sendf("stats count=%u sum=%u sumsq=%u", count, cur - stats_send_time, sumsq); sendf("stats count=%u sum=%u sumsq=%u"
, count, cur - stats_send_time - stats_sleep_time, sumsq);
if (cur < stats_send_time) if (cur < stats_send_time)
stats_send_time_high++; stats_send_time_high++;
stats_send_time = cur; stats_send_time = cur;
stats_sleep_time = 0;
count = sumsq = 0; count = sumsq = 0;
} }
DECL_TASK(stats_task); DECL_TASK(stats_task);

View File

@ -9,6 +9,7 @@ void move_request_size(int size);
void *oid_lookup(uint8_t oid, void *type); void *oid_lookup(uint8_t oid, void *type);
void *oid_alloc(uint8_t oid, void *type, uint16_t size); void *oid_alloc(uint8_t oid, void *type, uint16_t size);
void *oid_next(uint8_t *i, void *type); void *oid_next(uint8_t *i, void *type);
void stats_note_sleep(uint32_t sleep_time);
#define foreach_oid(pos,data,oidtype) \ #define foreach_oid(pos,data,oidtype) \
for (pos=-1; (data=oid_next(&pos, oidtype)); ) for (pos=-1; (data=oid_next(&pos, oidtype)); )

View File

@ -34,6 +34,12 @@ irq_restore(irqstatus_t flag)
asm volatile("msr primask, %0" :: "r" (flag) : "memory"); asm volatile("msr primask, %0" :: "r" (flag) : "memory");
} }
void
irq_wait(void)
{
asm volatile("cpsie i\n wfi\n cpsid i\n" ::: "memory");
}
void void
irq_poll(void) irq_poll(void)
{ {

View File

@ -9,6 +9,7 @@ void irq_disable(void);
void irq_enable(void); void irq_enable(void);
irqstatus_t irq_save(void); irqstatus_t irq_save(void);
void irq_restore(irqstatus_t flag); void irq_restore(irqstatus_t flag);
void irq_wait(void);
void irq_poll(void); void irq_poll(void);
#endif // irq.h #endif // irq.h

View File

@ -8,6 +8,7 @@
#include "board/irq.h" // irq_disable #include "board/irq.h" // irq_disable
#include "board/misc.h" // timer_from_us #include "board/misc.h" // timer_from_us
#include "board/timer_irq.h" // timer_dispatch_many #include "board/timer_irq.h" // timer_dispatch_many
#include "basecmd.h" // stats_note_sleep
#include "command.h" // shutdown #include "command.h" // shutdown
#include "sched.h" // sched_timer_kick #include "sched.h" // sched_timer_kick
@ -85,9 +86,23 @@ timer_dispatch_many(void)
void void
timer_task(void) timer_task(void)
{ {
static uint32_t last_timer;
uint32_t lst = last_timer;
irq_disable(); irq_disable();
timer_repeat_until = timer_read_time() + TIMER_IDLE_REPEAT_TICKS; uint32_t next = timer_get_next(), cur = timer_read_time();
if (lst != next) {
timer_repeat_until = cur + TIMER_IDLE_REPEAT_TICKS;
irq_enable();
last_timer = next;
return;
}
// Sleep the processor
irq_wait();
uint32_t post_sleep = timer_read_time();
timer_repeat_until = post_sleep + TIMER_IDLE_REPEAT_TICKS;
irq_enable(); irq_enable();
stats_note_sleep(post_sleep - cur);
} }
DECL_TASK(timer_task); DECL_TASK(timer_task);

View File

@ -2,5 +2,6 @@
#define __GENERIC_TIMER_IRQ_H #define __GENERIC_TIMER_IRQ_H
uint32_t timer_dispatch_many(void); uint32_t timer_dispatch_many(void);
uint32_t timer_get_next(void);
#endif // timer_irq.h #endif // timer_irq.h

View File

@ -46,12 +46,24 @@ irq_restore(irqstatus_t flag)
{ {
} }
void
irq_wait(void)
{
asm("slp 1");
}
static void static void
timer_set(uint32_t value) timer_set(uint32_t value)
{ {
CT_IEP.TMR_CMP0 = value; CT_IEP.TMR_CMP0 = value;
} }
uint32_t
timer_get_next(void)
{
return CT_IEP.TMR_CMP0;
}
uint32_t uint32_t
timer_read_time(void) timer_read_time(void)
{ {

View File

@ -135,8 +135,7 @@ static void
process_io(void) process_io(void)
{ {
for (;;) { for (;;) {
if (!(read_r31() & (1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET)))) asm("slp 1");
continue;
CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT); CT_INTC.SECR0 = (1 << KICK_PRU0_FROM_ARM_EVENT) | (1 << KICK_PRU0_EVENT);
check_can_send(); check_can_send();
check_can_read(); check_can_read();
@ -327,6 +326,10 @@ main(void)
, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS) , CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS)
; ;
// Allow PRU0 and PRU1 to wake from sleep
PRU0_CTRL.WAKEUP_EN = 1 << (WAKE_PRU0_IRQ + R31_IRQ_OFFSET);
PRU1_CTRL.WAKEUP_EN = 1 << (WAKE_PRU1_IRQ + R31_IRQ_OFFSET);
// Wait for PRU1 to be ready // Wait for PRU1 to be ready
memset(SHARED_MEM, 0, sizeof(*SHARED_MEM)); memset(SHARED_MEM, 0, sizeof(*SHARED_MEM));
writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING); writel(&SHARED_MEM->signal, SIGNAL_PRU0_WAITING);

View File

@ -18,6 +18,13 @@ timer_set(uint32_t value)
TC0->TC_CHANNEL[0].TC_RA = value; TC0->TC_CHANNEL[0].TC_RA = value;
} }
// Return the next scheduled wake up time
uint32_t
timer_get_next(void)
{
return TC0->TC_CHANNEL[0].TC_RA;
}
// Return the current time (in absolute clock ticks). // Return the current time (in absolute clock ticks).
uint32_t uint32_t
timer_read_time(void) timer_read_time(void)

View File

@ -47,6 +47,11 @@ irq_restore(irqstatus_t flag)
Interrupt_off = flag; Interrupt_off = flag;
} }
void
irq_wait(void)
{
}
void void
irq_poll(void) irq_poll(void)
{ {