From d189a3f3cf1b9aaaccf0996e8846aabe6e70cb4a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 25 Oct 2016 14:27:59 -0700 Subject: [PATCH] atmel-samd: Support auto-reset based on USB write activity. It will soft-reboot micropython after a burst of writes to the file system. This means that after you save files on your computer they will be automatically rerun. This can be disabled in the build by unsetting AUTORESET_TIMER in mpconfigboard.h. Using the REPL will also prevent the soft resets until you reset with CTRL-D manually. --- atmel-samd/Makefile | 3 + atmel-samd/access_vfs.c | 4 +- atmel-samd/autoreset.c | 87 +++++++++++++++++++ atmel-samd/autoreset.h | 39 +++++++++ .../boards/arduino_zero/mpconfigboard.h | 2 + .../feather_m0_adalogger/mpconfigboard.h | 2 + .../boards/feather_m0_basic/mpconfigboard.h | 2 + .../boards/metro_m0_flash/mpconfigboard.h | 2 + atmel-samd/internal_flash.c | 3 +- atmel-samd/main.c | 22 +++++ atmel-samd/mphalport.c | 38 ++++++++ lib/mp-readline/readline.h | 2 + lib/utils/pyexec.c | 1 + py/misc.h | 2 + 14 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 atmel-samd/autoreset.c create mode 100644 atmel-samd/autoreset.h diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index fbdfa3f93b..2cd917beb2 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -101,6 +101,7 @@ CFLAGS_CORTEX_M0 = \ -DEXTINT_CALLBACK_MODE=true \ -DUDD_ENABLE \ -DUSART_CALLBACK_MODE=true \ + -DTC_ASYNC=true \ -DUSB_DEVICE_LPM_SUPPORT CFLAGS = $(INC) -Wall -Werror -ansi -std=gnu99 -nostdlib $(CFLAGS_CORTEX_M0) $(COPT) @@ -135,6 +136,7 @@ SRC_ASF = $(addprefix asf/sam0/,\ drivers/system/interrupt/system_interrupt.c \ drivers/system/pinmux/pinmux.c \ drivers/system/system.c \ + drivers/tc/tc_interrupt.c \ drivers/tc/tc_sam_d_r/tc.c \ drivers/tcc/tcc.c \ drivers/usb/stack_interface/usb_device_udd.c \ @@ -144,6 +146,7 @@ SRC_ASF = $(addprefix asf/sam0/,\ SRC_C = \ access_vfs.c \ + autoreset.c \ builtin_open.c \ fatfs_port.c \ main.c \ diff --git a/atmel-samd/access_vfs.c b/atmel-samd/access_vfs.c index cfd5dfe541..a8cb33e9ca 100644 --- a/atmel-samd/access_vfs.c +++ b/atmel-samd/access_vfs.c @@ -27,11 +27,13 @@ #include #include "access_vfs.h" +#include "autoreset.h" #include "asf/common/services/usb/class/msc/device/udi_msc.h" #include "extmod/fsusermount.h" #include "lib/fatfs/diskio.h" #include "py/mpconfig.h" +#include "py/mphal.h" #include "py/mpstate.h" #include "py/misc.h" @@ -140,7 +142,6 @@ Ctrl_status vfs_usb_read_10(uint32_t addr, volatile uint16_t nb_sector) return CTRL_GOOD; } - //! This function transfers the USB MSC data to the memory //! //! @param addr Sector address to start write @@ -183,5 +184,6 @@ Ctrl_status vfs_usb_write_10(uint32_t addr, volatile uint16_t nb_sector) } } } + autoreset_start(); return CTRL_GOOD; } diff --git a/atmel-samd/autoreset.c b/atmel-samd/autoreset.c new file mode 100644 index 0000000000..ef7ca62a58 --- /dev/null +++ b/atmel-samd/autoreset.c @@ -0,0 +1,87 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "autoreset.h" + +#include "asf/sam0/drivers/tc/tc_interrupt.h" +#include "lib/mp-readline/readline.h" +#include "py/mphal.h" + +struct tc_module autoreset_timer; + +static bool autoreset_initialized = false; + +static bool autoreset_enabled = false; + +extern void inject_character(char); +void mp_keyboard_interrupt(void); + +void autoreset_callback(struct tc_module *const module_inst) { + if (autoreset_enabled) { + mp_keyboard_interrupt(); + inject_character(CHAR_CTRL_D); + } + + tc_stop_counter(&autoreset_timer); +} + +void autoreset_init() { + #ifdef AUTORESET_TIMER + struct tc_config config_tc; + tc_get_config_defaults(&config_tc); + config_tc.counter_size = TC_COUNTER_SIZE_16BIT; + config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV256; + config_tc.counter_16_bit.compare_capture_channel[0] = 0xFFFF; + tc_init(&autoreset_timer, AUTORESET_TIMER, &config_tc); + tc_enable(&autoreset_timer); + tc_register_callback(&autoreset_timer, autoreset_callback, TC_CALLBACK_CC_CHANNEL0); + tc_enable_callback(&autoreset_timer, TC_CALLBACK_CC_CHANNEL0); + tc_stop_counter(&autoreset_timer); + + autoreset_initialized = true; + #endif +} + +void autoreset_enable() { + autoreset_enabled = true; +} + +void autoreset_disable() { + autoreset_enabled = false; +} + +void autoreset_start() { + if (!autoreset_initialized) return; + + tc_stop_counter(&autoreset_timer); + tc_start_counter(&autoreset_timer); +} + +void autoreset_stop() { + if (!autoreset_initialized) return; + + tc_stop_counter(&autoreset_timer); +} diff --git a/atmel-samd/autoreset.h b/atmel-samd/autoreset.h new file mode 100644 index 0000000000..da9803fdef --- /dev/null +++ b/atmel-samd/autoreset.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__ + +#include "asf/sam0/drivers/tc/tc.h" + +void autoreset_callback(struct tc_module *const module_inst); +void autoreset_init(); +void autoreset_start(); +void autoreset_stop(); +void autoreset_enable(); +void autoreset_disable(); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__ diff --git a/atmel-samd/boards/arduino_zero/mpconfigboard.h b/atmel-samd/boards/arduino_zero/mpconfigboard.h index fbebaed2d1..42b4e76358 100644 --- a/atmel-samd/boards/arduino_zero/mpconfigboard.h +++ b/atmel-samd/boards/arduino_zero/mpconfigboard.h @@ -8,3 +8,5 @@ #define MICROPY_HW_LED_TX PIN_PA27 #define MICROPY_HW_LED_RX PIN_PB03 + +#define AUTORESET_TIMER TC5 diff --git a/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h b/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h index f820ce4a51..8af47af1e6 100644 --- a/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h +++ b/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h @@ -5,3 +5,5 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Adalogger" #define MICROPY_HW_MCU_NAME "samd21g18" + +#define AUTORESET_TIMER TC5 diff --git a/atmel-samd/boards/feather_m0_basic/mpconfigboard.h b/atmel-samd/boards/feather_m0_basic/mpconfigboard.h index a5b3ef0634..29027c0b8f 100644 --- a/atmel-samd/boards/feather_m0_basic/mpconfigboard.h +++ b/atmel-samd/boards/feather_m0_basic/mpconfigboard.h @@ -5,3 +5,5 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Basic" #define MICROPY_HW_MCU_NAME "samd21g18" + +#define AUTORESET_TIMER TC5 diff --git a/atmel-samd/boards/metro_m0_flash/mpconfigboard.h b/atmel-samd/boards/metro_m0_flash/mpconfigboard.h index a4e1e3fc54..3cbd0a641e 100644 --- a/atmel-samd/boards/metro_m0_flash/mpconfigboard.h +++ b/atmel-samd/boards/metro_m0_flash/mpconfigboard.h @@ -29,3 +29,5 @@ #define SPI_FLASH_PAD3_PINMUX PINMUX_PB11D_SERCOM4_PAD3 // SCK #define SPI_FLASH_CS PIN_PA13 #define SPI_FLASH_SERCOM SERCOM4 + +#define AUTORESET_TIMER TC5 diff --git a/atmel-samd/internal_flash.c b/atmel-samd/internal_flash.c index 68a3100c50..1bbbd23e8f 100644 --- a/atmel-samd/internal_flash.c +++ b/atmel-samd/internal_flash.c @@ -27,6 +27,7 @@ #include #include +#include "py/mphal.h" #include "py/obj.h" #include "py/runtime.h" #include "lib/fatfs/ff.h" @@ -40,7 +41,7 @@ #define TOTAL_INTERNAL_FLASH_SIZE 0x010000 #define INTERNAL_FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_INTERNAL_FLASH_SIZE) -#define INTERNAL_FLASH_PART1_START_BLOCK (0x100) +#define INTERNAL_FLASH_PART1_START_BLOCK (0x1) #define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / INTERNAL_FLASH_BLOCK_SIZE) static bool internal_flash_is_initialised = false; diff --git a/atmel-samd/main.c b/atmel-samd/main.c index df259d9c41..2a0dff8519 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -22,6 +22,7 @@ #include "asf/sam0/drivers/system/system.h" #include +#include "autoreset.h" #include "mpconfigboard.h" #include "modmachine_pin.h" @@ -172,6 +173,9 @@ static char *stack_top; static char heap[16384]; void reset_mp() { + autoreset_stop(); + autoreset_enable(); + // Sync the file systems in case any used RAM from the GC to cache. As soon // as we re-init the GC all bets are off on the cache. disk_ioctl(0, CTRL_SYNC, NULL); @@ -191,8 +195,18 @@ void reset_mp() { MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt); pin_init0(); +} +void start_mp() { + #ifdef AUTORESET_TIMER + mp_hal_stdout_tx_str("\r\n"); + mp_hal_stdout_tx_str("Auto-soft reset is on. Simply save files over USB to run them.\r\n"); + mp_hal_stdout_tx_str("Type anything into the REPL to disable and manually reset (CTRL-D) to re-enable.\r\n"); + #endif + + mp_hal_stdout_tx_str("boot.py output:\r\n"); pyexec_file("boot.py"); + mp_hal_stdout_tx_str("\r\nmain.py output:\r\n"); pyexec_file("main.py"); } @@ -216,11 +230,18 @@ int main(int argc, char **argv) { // as current dir. init_flash_fs(); + // Initialize the autoreset timer. It will automatically reset the repl + // after a burst of writes to the FS. + autoreset_init(); + // Start USB after getting everything going. #ifdef USB_REPL udc_start(); #endif + // Run boot and main. + start_mp(); + // Main script is finished, so now go into REPL mode. // The REPL mode can change, or it can request a soft reset. int exit_code = 0; @@ -233,6 +254,7 @@ int main(int argc, char **argv) { if (exit_code == PYEXEC_FORCED_EXIT) { mp_hal_stdout_tx_str("soft reboot\r\n"); reset_mp(); + start_mp(); } else if (exit_code != 0) { break; } diff --git a/atmel-samd/mphalport.c b/atmel-samd/mphalport.c index 7102bf220f..c84640fd5f 100644 --- a/atmel-samd/mphalport.c +++ b/atmel-samd/mphalport.c @@ -1,5 +1,6 @@ #include +#include "autoreset.h" #include "compiler.h" #include "asf/common/services/sleepmgr/sleepmgr.h" #include "asf/common/services/usb/class/cdc/device/udi_cdc.h" @@ -67,6 +68,40 @@ void usb_rts_notify(uint8_t port, bool set) { return; } +void inject_character(char c) { + // Introduce a critical section to avoid buffer corruption. We use + // cpu_irq_save instead of cpu_irq_disable because we don't know the + // current state of IRQs. They may have been turned off already and + // we don't want to accidentally turn them back on. + irqflags_t flags = cpu_irq_save(); + // If our buffer is full, then don't get another character otherwise + // we'll lose a previous character. + if (usb_rx_count >= USB_RX_BUF_SIZE) { + cpu_irq_restore(flags); + return; + } + + uint8_t current_tail = usb_rx_buf_tail; + // Pretend we've received a character so that any nested calls to + // this function have to consider the spot we've reserved. + if ((USB_RX_BUF_SIZE - 1) == usb_rx_buf_tail) { + // Reached the end of buffer, revert back to beginning of + // buffer. + usb_rx_buf_tail = 0x00; + } else { + usb_rx_buf_tail++; + } + // The count of characters present in receive buffer is + // incremented. + usb_rx_count++; + + // We put the next character where we expected regardless of whether + // the next character was already loaded in the buffer. + usb_rx_buf[current_tail] = c; + + cpu_irq_restore(flags); +} + void usb_rx_notify(void) { irqflags_t flags; @@ -129,6 +164,9 @@ int receive_usb() { return 0; } + // Disable autoreset if someone is using the repl. + autoreset_disable(); + // Copy from head. cpu_irq_disable(); int data = usb_rx_buf[usb_rx_buf_head]; diff --git a/lib/mp-readline/readline.h b/lib/mp-readline/readline.h index f73934d237..ffb0d0e1cf 100644 --- a/lib/mp-readline/readline.h +++ b/lib/mp-readline/readline.h @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "py/misc.h" + #define CHAR_CTRL_A (1) #define CHAR_CTRL_B (2) #define CHAR_CTRL_C (3) diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index f9bbf68a16..ce68e40b37 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -226,6 +226,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) { } else if (ret == CHAR_CTRL_B) { // reset friendly REPL mp_hal_stdout_tx_str("\r\n"); + mp_hal_stdout_tx_str("\r\n"); mp_hal_stdout_tx_str("Adafruit MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n"); // mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); goto input_restart; diff --git a/py/misc.h b/py/misc.h index e60665e591..b68f9cccb9 100644 --- a/py/misc.h +++ b/py/misc.h @@ -34,6 +34,8 @@ #include #include +#include "mpconfig.h" + typedef unsigned char byte; typedef unsigned int uint;