From f35aeb7ff472734567fbe15cdf5b89ad71007b04 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 6 Nov 2013 14:53:00 +0000 Subject: [PATCH] tests: Added a coroutine example Slapped together test, but demonstrates a nice trick that works on AVR for havbing multiple tasks concurently (coorperatively) Signed-off-by: Michel Pollet --- tests/atmega88_coroutine.c | 155 +++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 tests/atmega88_coroutine.c diff --git a/tests/atmega88_coroutine.c b/tests/atmega88_coroutine.c new file mode 100644 index 0000000..559fcf8 --- /dev/null +++ b/tests/atmega88_coroutine.c @@ -0,0 +1,155 @@ +/* + atmega88_coroutine.c + + Copyright 2008-2013 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#ifndef __AVR_CR_H__ +#define __AVR_CR_H__ +/* + * Smallest coroutine implementation for AVR. Takes + * 23 + (24 * tasks) bytes of SRAM to run. + * + * Use it like: + * + * AVR_TASK(mytask1, 32); + * AVR_TASK(mytask2, 48); + * ... + * void my_task_function() { + * do { + * AVR_YIELD(mytask1, 1); + * } while (1); + * } + * ... + * main() { + * AVR_TASK_START(mytask1, my_task_function); + * AVR_TASK_START(mytask2, my_other_task_function); + * do { + * AVR_RESUME(mytask1); + * AVR_RESUME(mytask2); + * } while (1); + * } + * NOTE: Do *not* use "static" on the function prototype, otherwise it + * will fail to link (compiler doesn't realize the "jmp" is referencing) + */ +#include +#include +static inline void _set_stack(register void * stack) +{ + asm volatile ( + "in r0, __SREG__" "\n\t" + "cli" "\n\t" + "out __SP_H__, %B0" "\n\t" + "out __SREG__, r0" "\n\t" + "out __SP_L__, %A0" "\n\t" + : : "e" (stack) : + ); +} + +jmp_buf g_caller; +#define AVR_TASK(_name, _stack_size) \ + struct { \ + jmp_buf jmp; \ + uint8_t running : 1; \ + uint8_t stack[_stack_size]; \ + } _name +#define AVR_TASK_START(_name, _entry) \ + if (!setjmp(g_caller)) { \ + _set_stack(_name.stack+sizeof(_name.stack));\ + asm volatile ("rjmp "#_entry); \ + } +#define AVR_YIELD(_name, _sleep) \ + _name.running = !_sleep; \ + if (!setjmp(_name.jmp)) \ + longjmp(g_caller, 1) +#define AVR_RESUME(_name) \ + if (!setjmp(g_caller)) \ + longjmp(_name.jmp, 1) +#endif /* __AVR_CR_H__ */ + + + +#include +#include +#include +#include + +#include "avr_mcu_section.h" +AVR_MCU(F_CPU, "atmega88"); + +static int uart_putchar(char c, FILE *stream) +{ + if (c == '\n') + uart_putchar('\r', stream); + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + return 0; +} + +static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, + _FDEV_SETUP_WRITE); + + +AVR_TASK(mytask1, 32); +AVR_TASK(mytask2, 48); + +uint16_t t = 0; + +void task1_function() { + uint16_t c = 0; + do { + c++; + if (c == 1000) { + c = 0; + printf("task1\n"); + t++; + } + AVR_YIELD(mytask1, 1); + } while (1); +} +void task2_function() { + uint16_t c = 0; + do { + c++; + if (c == 2000) { + c = 0; + printf("task2\n"); + } + AVR_YIELD(mytask2, 1); + } while (1); +} + + +int main(void) +{ + stdout = &mystdout; + + sei(); // Enable global interrupts + printf("Starting\n"); + AVR_TASK_START(mytask1, task1_function); + AVR_TASK_START(mytask2, task2_function); + do { + AVR_RESUME(mytask1); + AVR_RESUME(mytask2); + } while (t < 500); + + // exit simavr + cli(); + sleep_mode(); +} + -- 2.39.5