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;