diff --git a/.gitmodules b/.gitmodules index c152e9e7747fa..ee1b2bd61c2a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -36,3 +36,6 @@ [submodule "lib/nxp_driver"] path = lib/nxp_driver url = https://github.com/hathach/nxp_driver.git +[submodule "ports/gd32vf103/gd32vf103inator"] + path = ports/gd32vf103/gd32vf103inator + url = https://github.com/esmil/gd32vf103inator.git diff --git a/.travis.yml b/.travis.yml index c9fcc21336efd..8d1320ba5e2c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -157,7 +157,7 @@ jobs: - MICROPYPATH=examples/natmod/features2 ./ports/unix/micropython-coverage -m features2 - (cd tests && ./run-natmodtests.py extmod/{btree*,framebuf*,uheapq*,ure*,uzlib*}.py) # run coveralls coverage analysis (try to, even if some builds/tests failed) - - (cd ports/unix && coveralls --root ../.. --build-root . --gcov $(which gcov) --gcov-options '\-o build-coverage/' --include py --include extmod) + - '[ "x$NOCOVERALLS" != x ] || (cd ports/unix && coveralls --root ../.. --build-root . --gcov $(which gcov) --gcov-options ''\-o build-coverage/'' --include py --include extmod)' after_failure: - tests/run-tests --print-failures @@ -394,3 +394,15 @@ jobs: script: - make ${MAKEOPTS} -C ports/powerpc UART=potato - make ${MAKEOPTS} -C ports/powerpc UART=lpc_serial + + # gd32vf103 port + - stage: test + name: "gd32vf103 port build" + os: linux + dist: focal + install: + - sudo apt-get install gcc-riscv64-unknown-elf gcc-riscv64-linux-gnu + script: + - make ${MAKEOPTS} -C ports/gd32vf103 submodules + - make ${MAKEOPTS} -C ports/gd32vf103 BUILD=build-baremetal + - make ${MAKEOPTS} -C ports/gd32vf103 BUILD=build-linux CROSS_COMPILE=riscv64-linux-gnu- diff --git a/extmod/extmod.mk b/extmod/extmod.mk index e312acba86db4..b000b058d7bd4 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -37,6 +37,8 @@ SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\ lfs2.c \ lfs2_util.c \ ) + +$(BUILD)/$(LITTLEFS_DIR)/lfs2.o: CFLAGS += -Wno-missing-field-initializers endif ################################################################################ diff --git a/extmod/machine_i2c.c b/extmod/machine_i2c.c index 9203f16f6d785..12c9abbcbaece 100644 --- a/extmod/machine_i2c.c +++ b/extmod/machine_i2c.c @@ -311,8 +311,8 @@ STATIC void mp_machine_soft_i2c_print(const mp_print_t *print, mp_obj_t self_in, STATIC void machine_i2c_obj_init_helper(machine_i2c_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_scl, ARG_sda, ARG_freq, ARG_timeout }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 255} }, }; diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 57f69433a1a97..a8b8089ca96cb 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -36,6 +36,7 @@ #include "py/runtime.h" #include "extmod/modbluetooth.h" #include +#include #if MICROPY_PY_BLUETOOTH diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 1d557a6a84b5a..7a73b4ee92419 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -28,6 +28,7 @@ #include #include +#include #include "py/objlist.h" #include "py/runtime.h" diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 811258424a3bf..c5fbf12e42b9d 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -506,6 +506,7 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset); } // Fall thru to return uctypes struct object + MP_FALLTHROUGH } case PTR: { mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); @@ -627,7 +628,7 @@ STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { return mp_obj_new_int((mp_int_t)(uintptr_t)p); } } - /* fallthru */ + MP_FALLTHROUGH default: return MP_OBJ_NULL; // op not supported diff --git a/extmod/moduselect.c b/extmod/moduselect.c index 80beb8e09b8aa..de704b28f9f55 100644 --- a/extmod/moduselect.c +++ b/extmod/moduselect.c @@ -29,6 +29,7 @@ #if MICROPY_PY_USELECT #include +#include #include "py/runtime.h" #include "py/obj.h" diff --git a/extmod/moduwebsocket.c b/extmod/moduwebsocket.c index 34b520f33706a..ee5493f76f8c4 100644 --- a/extmod/moduwebsocket.c +++ b/extmod/moduwebsocket.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "py/runtime.h" #include "py/stream.h" diff --git a/extmod/modwebrepl.c b/extmod/modwebrepl.c index a8430bafb3c8e..6173f809d61e4 100644 --- a/extmod/modwebrepl.c +++ b/extmod/modwebrepl.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "py/runtime.h" #include "py/stream.h" diff --git a/extmod/re1.5/compilecode.c b/extmod/re1.5/compilecode.c index 3f54b3993fd36..c4d12af87a32e 100644 --- a/extmod/re1.5/compilecode.c +++ b/extmod/re1.5/compilecode.c @@ -29,6 +29,7 @@ static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) prog->len++; break; } + MP_FALLTHROUGH default: term = PC; EMIT(PC++, Char); diff --git a/extmod/re1.5/recursiveloop.c b/extmod/re1.5/recursiveloop.c index bb337decfbc95..f8cb92629200a 100644 --- a/extmod/re1.5/recursiveloop.c +++ b/extmod/re1.5/recursiveloop.c @@ -22,6 +22,7 @@ recursiveloop(char *pc, const char *sp, Subject *input, const char **subp, int n case Char: if(*sp != *pc++) return 0; + MP_FALLTHROUGH case Any: sp++; continue; diff --git a/extmod/vfs.c b/extmod/vfs.c index 3cb7af1b434f9..1be1c006b2426 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -26,6 +26,7 @@ #include #include +#include #include "py/runtime.h" #include "py/objstr.h" diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 95b7ad9944116..8f1af2f99218b 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -33,6 +33,7 @@ #endif #include +#include #include "py/runtime.h" #include "py/mperrno.h" #include "lib/oofatfs/ff.h" diff --git a/extmod/vfs_fat_file.c b/extmod/vfs_fat_file.c index 537101d00f406..10d9ef2a08861 100644 --- a/extmod/vfs_fat_file.c +++ b/extmod/vfs_fat_file.c @@ -28,6 +28,7 @@ #if MICROPY_VFS && MICROPY_VFS_FAT #include +#include #include "py/runtime.h" #include "py/stream.h" diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index 9cf3eb11083cf..dd78269a460ab 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -35,7 +35,7 @@ enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead, LFS_MAKE_ARG_mtime }; static const mp_arg_t lfs_make_allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_readsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_progsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_lookahead, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index 2c8ca2de0cecc..cc8f059082c8e 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include #include diff --git a/mpy-cross/Makefile b/mpy-cross/Makefile index f80ee761b7bcf..971f2f81aa593 100644 --- a/mpy-cross/Makefile +++ b/mpy-cross/Makefile @@ -18,7 +18,7 @@ INC += -I$(TOP) # compiler settings CWARN = -Wall -Werror -CWARN += -Wpointer-arith -Wuninitialized +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith CFLAGS = $(INC) $(CWARN) -std=gnu99 $(CFLAGS_MOD) $(COPT) $(CFLAGS_EXTRA) CFLAGS += -fdata-sections -ffunction-sections -fno-asynchronous-unwind-tables diff --git a/ports/gd32vf103/Makefile b/ports/gd32vf103/Makefile new file mode 100644 index 0000000000000..2939bcb44001f --- /dev/null +++ b/ports/gd32vf103/Makefile @@ -0,0 +1,104 @@ +include ../../py/mkenv.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# include py core make definitions +include $(TOP)/py/py.mk + +GIT_SUBMODULES = ports/gd32vf103/gd32vf103inator + +CROSS_COMPILE = riscv64-unknown-elf- +OBJDUMP = $(CROSS_COMPILE)objdump + +DFU_UTIL = dfu-util +DFU_DEVICE = 1d50:613e + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += -Igd32vf103inator/include +INC += -Igd32vf103inator/std + +ARCH = rv32imac +ABI = ilp32 +CODEMODEL = medlow + +BOOTLOADER = 4*1024 +FLASH_SIZE = 128*1024 +RAM_SIZE = 32*1024 + +CORECLOCK = 96000000 +HXTAL = 8000000 + +ARCHFLAGS = -march=$(ARCH) -mabi=$(ABI) -mcmodel=$(CODEMODEL) -fno-pie +WARNINGS = -Wall -Wextra -Wshadow -Wformat=2 -Wformat-truncation=2 -Wno-unused-parameter +DIMENSIONS = -DBOOTLOADER=$(BOOTLOADER) -DFLASH_SIZE=$(FLASH_SIZE) -DRAM_SIZE=$(RAM_SIZE) +CLOCKS = -DHXTAL=$(HXTAL) -DCORECLOCK=$(CORECLOCK) + +CFLAGS = $(COPT) -std=c99 $(ARCHFLAGS) $(WARNINGS) +CFLAGS += $(INC) $(DIMENSIONS) $(CLOCKS) -D_LIBC_LIMITS_H_ +CFLAGS += -ffreestanding #-ftls-model=local-exec +LDFLAGS = $(COPT) $(ARCHFLAGS) -static -nostdlib -Tgd32vf103inator/gd32vf103.ld +LDFLAGS += -Wl,-O1,--gc-sections,--relax,--build-id=none,-Map=$@.map,--cref + +CSUPEROPT = -Os # save some code space + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +COPT = -Os -ggdb +else +COPT = -Os -flto -DNDEBUG +CFLAGS += -fno-common -ffunction-sections -fdata-sections +endif + + +LIBS = -lgcc + +SRC_C = \ + main.c \ + usbacm.c \ + mphalport.c \ + modutime.c \ + modmachine.c \ + pin.c \ + gd32vf103inator/lib/rcu.c \ + gd32vf103inator/lib/eclic.c \ + gd32vf103inator/lib/mtimer.c \ + gd32vf103inator/lib/gpio.c \ + lib/utils/printf.c \ + lib/utils/stdout_helpers.c \ + lib/utils/pyexec.c \ + lib/mp-readline/readline.c \ + lib/libc/string0.c + +# List of sources for qstr extraction +SRC_QSTR += modutime.c pin.c modmachine.c + +OBJ = $(BUILD)/gd32vf103inator/start.o \ + $(PY_O) \ + $(patsubst %.c,$(BUILD)/%.o,$(SRC_C)) + +.PHONY: all dump dfu romdfu + +all: $(BUILD)/firmware.bin + +$(BUILD)/firmware.elf: $(OBJ) + $(ECHO) "LINK $@" + $(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(SIZE) $@ + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(ECHO) "OBJCOPY $@" + $(Q)$(OBJCOPY) -O binary $^ $@ + +dump: $(BUILD)/firmware.elf + $(OBJDUMP) -x -d -j .init -j .text -j .data $< | $(PAGER) + +dfu: $(BUILD)/firmware.bin + $(Q)$(DFU_UTIL) -d $(DFU_DEVICE) -D $< -R + +romdfu: $(BUILD)/firmware.bin + $(Q)$(DFU_UTIL) -d 28e9:0189 -a 0 --dfuse-address 0x08000000:leave -D $< + +include $(TOP)/py/mkrules.mk diff --git a/ports/gd32vf103/README.md b/ports/gd32vf103/README.md new file mode 100644 index 0000000000000..356fc4b3effc1 --- /dev/null +++ b/ports/gd32vf103/README.md @@ -0,0 +1,47 @@ +# The minimal port + +This port is intended to be a minimal MicroPython port that actually runs. +It can run under Linux (or similar) and on any STM32F4xx MCU (eg the pyboard). + +## Building and running Linux version + +By default the port will be built for the host machine: + + $ make + +To run the executable and get a basic working REPL do: + + $ make run + +## Building for an STM32 MCU + +The Makefile has the ability to build for a Cortex-M CPU, and by default +includes some start-up code for an STM32F4xx MCU and also enables a UART +for communication. To build: + + $ make CROSS=1 + +If you previously built the Linux version, you will need to first run +`make clean` to get rid of incompatible object files. + +Building will produce the build/firmware.dfu file which can be programmed +to an MCU using: + + $ make CROSS=1 deploy + +This version of the build will work out-of-the-box on a pyboard (and +anything similar), and will give you a MicroPython REPL on UART1 at 9600 +baud. Pin PA13 will also be driven high, and this turns on the red LED on +the pyboard. + +## Building without the built-in MicroPython compiler + +This minimal port can be built with the built-in MicroPython compiler +disabled. This will reduce the firmware by about 20k on a Thumb2 machine, +and by about 40k on 32-bit x86. Without the compiler the REPL will be +disabled, but pre-compiled scripts can still be executed. + +To test out this feature, change the `MICROPY_ENABLE_COMPILER` config +option to "0" in the mpconfigport.h file in this directory. Then +recompile and run the firmware and it will execute the frozentest.py +file. diff --git a/ports/gd32vf103/gd32vf103inator b/ports/gd32vf103/gd32vf103inator new file mode 160000 index 0000000000000..e6b1384d0c160 --- /dev/null +++ b/ports/gd32vf103/gd32vf103inator @@ -0,0 +1 @@ +Subproject commit e6b1384d0c1607ab6b902f15e55745ac9ebd8547 diff --git a/ports/gd32vf103/main.c b/ports/gd32vf103/main.c new file mode 100644 index 0000000000000..9349c71caff1c --- /dev/null +++ b/ports/gd32vf103/main.c @@ -0,0 +1,170 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 +#include +#include + +#include "py/compile.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/stackctrl.h" +#include "py/mphal.h" +#include "lib/mp-readline/readline.h" +#include "lib/utils/pyexec.h" + +#include "lib/rcu.h" +#include "lib/eclic.h" +#include "lib/gpio.h" + +#include "usbacm.h" + +static void **stack_top; +static char heap[16384]; + +#ifdef __interrupt +void trap_entry(void) { + printf("TRAP!\n"); + while (1) { + /* forever */ + } +} +#endif + +int main(void) { + rcu_sysclk_init(); + eclic_init(); + eclic_global_interrupt_enable(); + + #if 0 + /* turn on power to GPIOA */ + RCU->APB2EN |= RCU_APB2EN_PAEN; + + gpio_pin_set(GPIO_PA1); + gpio_pin_config(GPIO_PA1, GPIO_MODE_PP_50MHZ); + gpio_pin_set(GPIO_PA2); + gpio_pin_config(GPIO_PA2, GPIO_MODE_PP_50MHZ); + #endif + + usbacm_init(); + + (void)mp_hal_stdin_rx_chr(); + + /* save current stack pointer to stack_top */ + __asm("sw sp, %0\n" : "=m" (stack_top)); + +soft_reset: + mp_stack_set_top(stack_top); + mp_stack_set_limit(1024); + + gc_init(heap, heap + sizeof(heap)); + + mp_init(); + /* sys.path = [''] */ + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), 0); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); + /* sys.argv = [] */ + mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_argv), 0); + + readline_init0(); + + while (1) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl()) { + break; + } + } else { + if (pyexec_friendly_repl()) { + break; + } + } + } + gc_sweep_all(); + printf("soft reboot\n"); + goto soft_reset; +} + +void gc_collect(void) { + void *sregs[12]; + void **sp; + + gc_collect_start(); + + /* push callee-save registers to + * stack before sweeping it */ + __asm( + "sw s0, 0+%1 \n" + "sw s1, 4+%1 \n" + "sw s2, 8+%1 \n" + "sw s3, 12+%1 \n" + "sw s4, 16+%1 \n" + "sw s5, 20+%1 \n" + "sw s6, 24+%1 \n" + "sw s7, 28+%1 \n" + "sw s8, 32+%1 \n" + "sw s9, 36+%1 \n" + "sw s10, 40+%1 \n" + "sw s11, 44+%1 \n" + "mv %0, sp \n" + : "=r" (sp), "=o" (sregs) + ); + + gc_collect_root(sp, stack_top - sp); + gc_collect_end(); +} + +mp_lexer_t *mp_lexer_new_from_file(const char *filename) { + mp_raise_OSError(MP_ENOENT); +} + +mp_import_stat_t mp_import_stat(const char *path) { + return MP_IMPORT_STAT_NO_EXIST; +} + +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); + +void nlr_jump_fail(void *val) { + while (1) { + /* forever */ + } +} + +void NORETURN __fatal_error(const char *msg) { + printf("%s\n", msg); + while (1) { + /* forever */ + } +} + +#ifndef NDEBUG +void __assert_func(const char *file, int line, const char *func, const char *expr) { + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + __fatal_error("Assertion failed"); +} +#endif diff --git a/ports/gd32vf103/modmachine.c b/ports/gd32vf103/modmachine.c new file mode 100644 index 0000000000000..4939b4608c440 --- /dev/null +++ b/ports/gd32vf103/modmachine.c @@ -0,0 +1,108 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * Copyright (c) 2019,2020 Emil Renner Berthing + * + * 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 "riscv/bits.h" +#include "gd32vf103/info.h" +#include "gd32vf103/dbg.h" +#include "lib/eclic.h" + +#include "py/runtime.h" +#include "extmod/machine_mem.h" +#include "lib/utils/pyexec.h" + +#include "pin.h" + +STATIC mp_obj_t machine_freq(void) { + return MP_OBJ_NEW_SMALL_INT(CORECLOCK); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_freq_obj, machine_freq); + +STATIC mp_obj_t machine_unique_id(void) { + byte *id = (byte *)INFO->ID; + return mp_obj_new_bytes(id, 12); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); + +STATIC mp_obj_t machine_reset(void) { + DBG->KEY = DBG_KEY_UNLOCK; + DBG->CMD = DBG_CMD_RESET; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); + +STATIC mp_obj_t machine_soft_reset(void) { + pyexec_system_exit = PYEXEC_FORCED_EXIT; + mp_raise_type(&mp_type_SystemExit); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_soft_reset_obj, machine_soft_reset); + +STATIC mp_obj_t machine_idle(void) { + wait_for_interrupt(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +STATIC mp_obj_t machine_disable_irq(void) { + unsigned long mstatus = eclic_global_interrupt_disable_save(); + return mp_obj_new_bool(mstatus & CSR_MSTATUS_MIE); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(uint n_args, const mp_obj_t *arg) { + if (n_args == 0 || mp_obj_is_true(arg[0])) { + eclic_global_interrupt_enable(); + } else { + eclic_global_interrupt_disable(); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_enable_irq_obj, 0, 1, machine_enable_irq); + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_soft_reset), MP_ROM_PTR(&machine_soft_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, + + { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, + + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&machine_module_globals, +}; + +/* vim: set ts=4 sw=4 et: */ diff --git a/ports/gd32vf103/modutime.c b/ports/gd32vf103/modutime.c new file mode 100644 index 0000000000000..8d6a4059446f6 --- /dev/null +++ b/ports/gd32vf103/modutime.c @@ -0,0 +1,47 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * 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 "extmod/utime_mphal.h" + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t mp_module_utime = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&time_module_globals, +}; diff --git a/ports/gd32vf103/mpconfigport.h b/ports/gd32vf103/mpconfigport.h new file mode 100644 index 0000000000000..896b4ca9c0dfb --- /dev/null +++ b/ports/gd32vf103/mpconfigport.h @@ -0,0 +1,90 @@ +#include + +#define MICROPY_ENABLE_COMPILER (1) + +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_USE_INTERNAL_PRINTF (1) + +#ifdef __GNUC__ +#define MICROPY_OPT_COMPUTED_GOTO (1) +#else +#define MICROPY_OPT_COMPUTED_GOTO (0) +#endif +#define MICROPY_OPT_MPZ_BITWISE (1) + +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) +#define MICRAPY_STACK_CHECK (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) + +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) + +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_STACK_ENTRY_TYPE uint16_t +#define MICROPY_GC_ALLOC_THRESHOLD (0) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) + +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_COLLECTIONS (0) +#define MICROPY_PY_MATH (0) +#define MICROPY_PY_CMATH (0) +#define MICROPY_PY_IO (0) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_UTIME_TICKS_PERIOD (1U << 30) + +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) + +#if 0 // for tests +#include +#define MICROPY_CPYTHON_COMPAT (1) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MP_SSIZE_MAX INT_MAX +#undef MICROPY_ERROR_REPORTING +#endif + +// type definitions for the specific machine + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)(p)) + +#define INT_FMT "%d" +#define UINT_FMT "%u" +typedef int mp_int_t; // must be pointer size +typedef unsigned int mp_uint_t; // must be pointer size + +typedef long mp_off_t; + +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) }, + +extern const struct _mp_obj_module_t mp_module_machine; +extern const struct _mp_obj_module_t mp_module_utime; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&mp_module_machine) }, \ + { MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, + +// We need to provide a declaration/definition of alloca() +#include + +#define MICROPY_HW_BOARD_NAME "LonganNano" +#define MICROPY_HW_MCU_NAME "gd32vf103" + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; diff --git a/ports/gd32vf103/mphalport.c b/ports/gd32vf103/mphalport.c new file mode 100644 index 0000000000000..e8bfe3c0aafe7 --- /dev/null +++ b/ports/gd32vf103/mphalport.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 +#include + +#include "py/mphal.h" + +#include "lib/mtimer.h" + +/* sanity check */ +#if MICROPY_PY_UTIME_TICKS_PERIOD != (1U << 30) +#error This implementation is only correct for MICROPY_PY_UTIME_TICKS_PERIOD == 2^30 +#endif + +void mp_hal_delay_ms(mp_uint_t ms) { + mtimer_delay(ms * (MTIMER_FREQ / 1000)); +} + +void mp_hal_delay_us(mp_uint_t us) { + mtimer_delay(us * (MTIMER_FREQ / 1000000)); +} + +mp_uint_t mp_hal_ticks_ms(void) { + uint64_t mtime = mtimer_mtime(); + + /* return (mtime / (CORECLOCK/4000)) % 2^30 + * using a / b = a * (2^34 / b) / 2^34 + */ + mtime *= ((4000ULL << 34) - 1) / CORECLOCK + 1; + return mtime >> 34; +} + +mp_uint_t mp_hal_ticks_us(void) { + uint64_t mtime = mtimer_mtime(); + + /* return (mtime / (CORECLOCK/4000000)) % 2^30 + * using a / b = a * (2^34 / b) / 2^34 + */ + mtime *= ((4000000ULL << 34) - 1) / CORECLOCK + 1; + return mtime >> 34; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return MTIMER->mtime_lo % MICROPY_PY_UTIME_TICKS_PERIOD; +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/ports/gd32vf103/mphalport.h b/ports/gd32vf103/mphalport.h new file mode 100644 index 0000000000000..5b48fea72c2a8 --- /dev/null +++ b/ports/gd32vf103/mphalport.h @@ -0,0 +1,2 @@ +static inline void mp_hal_set_interrupt_char(char c) { +} diff --git a/ports/gd32vf103/pin.c b/ports/gd32vf103/pin.c new file mode 100644 index 0000000000000..41e9fdb363173 --- /dev/null +++ b/ports/gd32vf103/pin.c @@ -0,0 +1,312 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Emil Renner Berthing + * + * 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 +#include + +#include "py/runtime.h" +#include "extmod/virtpin.h" + +#include "pin.h" + +const pin_obj_t pin_base[80] = { + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, + {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, {{ &pin_type }}, +}; + +static int pin_parse(const char *str) { + int ret; + + if (str[0] < 'A' || str[0] > 'E') { + return -1; + } + + ret = (str[0] - 'A') << 4; + + if (str[1] == '1' && str[2] >= '0' && str[2] <= '5') { + if (str[3] != '\0') { + return -1; + } + ret |= str[2] - '0' + 10; + } else if (str[1] >= '0' && str[1] <= '9') { + if (str[2] != '\0') { + return -1; + } + ret |= str[1] - '0'; + } else { + return -1; + } + + return ret; +} + +static const pin_obj_t *pin_find(mp_obj_t user_obj) { + // If pin is SMALL_INT + if (mp_obj_is_small_int(user_obj)) { + uint value = MP_OBJ_SMALL_INT_VALUE(user_obj); + + if (value >= MP_ARRAY_SIZE(pin_base)) { + mp_raise_msg_varg(&mp_type_ValueError, + MP_ERROR_TEXT("Pin(%u) doesn't exist"), value); + } + return &pin_base[value]; + } + + // If a pin was provided, then use it + if (mp_obj_is_type(user_obj, &pin_type)) { + return MP_OBJ_TO_PTR(user_obj); + } + + const char *str = mp_obj_str_get_str(user_obj); + int pin = pin_parse(str); + + if (pin > 0) { + return &pin_base[pin]; + } + + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin('%s') doesn't exist"), str); +} + +STATIC mp_obj_t pin_obj_init_helper(const pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_value, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get pin + gpio_pin_t pin = pin_from_obj(self); + + // get mode + uint mode = args[0].u_int; + if (mode > 0xfU || mode == 0xcU) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin mode: %u"), mode); + } + + // enable the peripheral clock for the port of this pin + gpio_pin_clock_enable(pin); + + // if given, set the pin value before initialising to prevent glitches + if (args[1].u_obj != MP_OBJ_NULL) { + if (mp_obj_is_true(args[1].u_obj)) { + gpio_pin_set(pin); + } else { + gpio_pin_clear(pin); + } + } + + // configure the GPIO as requested + gpio_pin_config(pin, mode); + + return mp_const_none; +} + +/// \classmethod \constructor(id, ...) +/// Create a new Pin object associated with the id. If additional arguments are given, +/// they are used to initialise the pin. See `init`. +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // Run an argument through the mapper and return the result. + const pin_obj_t *pin = pin_find(args[0]); + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + pin_obj_init_helper(pin, n_args - 1, args + 1, &kw_args); + } + + return MP_OBJ_FROM_PTR(pin); +} + +/// \method __str__() +/// Return a string describing the pin object. +STATIC void pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + static const qstr pin_mode_name[16] = { + MP_QSTR_IN_ANALOG, + MP_QSTR_PP_10MHZ, + MP_QSTR_PP_2MHZ, + MP_QSTR_PP_50MHZ, + MP_QSTR_IN_FLOAT, + MP_QSTR_OD_10MHZ, + MP_QSTR_OD_2MHZ, + MP_QSTR_OD_50MHZ, + MP_QSTR_IN_PULL, + MP_QSTR_AF_PP_10MHZ, + MP_QSTR_AF_PP_2MHZ, + MP_QSTR_AF_PP_50MHZ, + 0, // reserved + MP_QSTR_AF_OD_10MHZ, + MP_QSTR_AF_OD_2MHZ, + MP_QSTR_AF_OD_50MHZ, + }; + const pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_pin_t pin = pin_from_obj(self); + + mp_printf(print, "Pin('%c%u', mode=Pin.%q, value=%u)", + 'A' + gpio_pin_port_nr(pin), gpio_pin_nr(pin), + pin_mode_name[gpio_pin_mode(pin)], + !!gpio_pin_get(pin)); +} + +// fast method for getting/setting pin value +STATIC mp_obj_t pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + const pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_pin_t pin = pin_from_obj(self); + + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(!!gpio_pin_high(pin)); + } else { + // set pin + if (mp_obj_is_true(args[0])) { + gpio_pin_set(pin); + } else { + gpio_pin_clear(pin); + } + return mp_const_none; + } +} + +STATIC mp_obj_t pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return pin_obj_init_helper(MP_OBJ_TO_PTR(args[0]), n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(pin_init_obj, 1, pin_obj_init); + +/// \method value([value]) +/// Get or set the digital logic level of the pin: +/// +/// - With no argument, return 0 or 1 depending on the logic level of the pin. +/// - With `value` given, set the logic level of the pin. `value` can be +/// anything that converts to a boolean. If it converts to `True`, the pin +/// is set high, otherwise it is set low. +STATIC mp_obj_t pin_value(size_t n_args, const mp_obj_t *args) { + return pin_call(args[0], n_args - 1, 0, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_value_obj, 1, 2, pin_value); + +STATIC mp_obj_t pin_off(mp_obj_t self_in) { + const pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_pin_clear(pin_from_obj(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_off_obj, pin_off); + +STATIC mp_obj_t pin_on(mp_obj_t self_in) { + const pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_pin_set(pin_from_obj(self)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_on_obj, pin_on); + +STATIC const mp_rom_map_elem_t pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&pin_off_obj) }, + { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&pin_on_obj) }, + + // Legacy names as used by pyb.Pin + { MP_ROM_QSTR(MP_QSTR_low), MP_ROM_PTR(&pin_off_obj) }, + { MP_ROM_QSTR(MP_QSTR_high), MP_ROM_PTR(&pin_on_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN_ANALOG), MP_ROM_INT(GPIO_MODE_IN_ANALOG) }, + { MP_ROM_QSTR(MP_QSTR_PP_10MHZ), MP_ROM_INT(GPIO_MODE_PP_10MHZ) }, + { MP_ROM_QSTR(MP_QSTR_PP_2MHZ), MP_ROM_INT(GPIO_MODE_PP_2MHZ) }, + { MP_ROM_QSTR(MP_QSTR_PP_50MHZ), MP_ROM_INT(GPIO_MODE_PP_50MHZ) }, + { MP_ROM_QSTR(MP_QSTR_IN_FLOAT), MP_ROM_INT(GPIO_MODE_IN_FLOAT) }, + { MP_ROM_QSTR(MP_QSTR_OD_10MHZ), MP_ROM_INT(GPIO_MODE_OD_10MHZ) }, + { MP_ROM_QSTR(MP_QSTR_OD_2MHZ), MP_ROM_INT(GPIO_MODE_OD_2MHZ) }, + { MP_ROM_QSTR(MP_QSTR_OD_50MHZ), MP_ROM_INT(GPIO_MODE_OD_50MHZ) }, + { MP_ROM_QSTR(MP_QSTR_IN_PULL), MP_ROM_INT(GPIO_MODE_IN_PULL) }, + { MP_ROM_QSTR(MP_QSTR_AF_PP_10MHZ), MP_ROM_INT(GPIO_MODE_AF_PP_10MHZ) }, + { MP_ROM_QSTR(MP_QSTR_AF_PP_2MHZ), MP_ROM_INT(GPIO_MODE_AF_PP_2MHZ) }, + { MP_ROM_QSTR(MP_QSTR_AF_PP_50MHZ), MP_ROM_INT(GPIO_MODE_AF_PP_50MHZ) }, + { MP_ROM_QSTR(MP_QSTR_AF_OD_10MHZ), MP_ROM_INT(GPIO_MODE_AF_OD_10MHZ) }, + { MP_ROM_QSTR(MP_QSTR_AF_OD_2MHZ), MP_ROM_INT(GPIO_MODE_AF_OD_2MHZ) }, + { MP_ROM_QSTR(MP_QSTR_AF_OD_50MHZ), MP_ROM_INT(GPIO_MODE_AF_OD_50MHZ) }, +}; + +STATIC MP_DEFINE_CONST_DICT(pin_locals_dict, pin_locals_dict_table); + +STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + const pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + gpio_pin_t pin = pin_from_obj(self); + + switch (request) { + case MP_PIN_READ: + return !!gpio_pin_high(pin); + case MP_PIN_WRITE: + if (arg) { + gpio_pin_set(pin); + } else { + gpio_pin_clear(pin); + } + return 0; + } + return -1; +} + +STATIC const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +const mp_obj_type_t pin_type = { + { &mp_type_type }, + .name = MP_QSTR_Pin, + .print = pin_print, + .make_new = mp_pin_make_new, + .call = pin_call, + .protocol = &pin_pin_p, + .locals_dict = (mp_obj_dict_t *)&pin_locals_dict, +}; + +/* vim: set ts=4 sw=4 et: */ diff --git a/ports/gd32vf103/pin.h b/ports/gd32vf103/pin.h new file mode 100644 index 0000000000000..60547ee8fe7d5 --- /dev/null +++ b/ports/gd32vf103/pin.h @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Emil Renner Berthing + * + * 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_GD32VF103_PIN_H +#define MICROPY_GD32VF103_PIN_H + +#include "py/obj.h" + +#include "lib/gpio.h" + +extern const mp_obj_type_t pin_type; + +typedef struct { + mp_obj_base_t base; +} pin_obj_t; + +extern const pin_obj_t pin_base[80]; + +static inline gpio_pin_t pin_from_obj(const pin_obj_t *obj) { + ptrdiff_t v = obj - pin_base; + return GPIO_PIN(v >> 4, v & 0xf); +} + +#endif // MICROPY_GD32VF103_PIN_H diff --git a/ports/gd32vf103/qstrdefsport.h b/ports/gd32vf103/qstrdefsport.h new file mode 100644 index 0000000000000..3ba897069bf73 --- /dev/null +++ b/ports/gd32vf103/qstrdefsport.h @@ -0,0 +1 @@ +// qstrs specific to this port diff --git a/ports/gd32vf103/uart0.c b/ports/gd32vf103/uart0.c new file mode 100644 index 0000000000000..0cf88000f0f3f --- /dev/null +++ b/ports/gd32vf103/uart0.c @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 +#include +#include + +#include "py/mpconfig.h" + +#include "gd32vf103/rcu.h" +#include "gd32vf103/usart.h" + +#include "lib/eclic.h" +#include "lib/gpio.h" + +#include "uart0.h" + +int mp_hal_stdin_rx_chr(void) { + while (!(USART0->STAT & USART_STAT_RBNE)) { + /* wait */ + } + + return USART0->DATA; +} + +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + for (; len > 0; len--, str++) { + while (!(USART0->STAT & USART_STAT_TBE)) { + /* wait */ + } + USART0->DATA = *str; + } +} + +void uart0_init(uint32_t pclk, uint32_t target) { + /* enable GPIO clock */ + RCU->APB2EN |= RCU_APB2EN_PAEN; + /* enable USART clock */ + RCU->APB2EN |= RCU_APB2EN_USART0EN; + + gpio_pin_config(GPIO_PA9, GPIO_MODE_AF_PP_50MHZ); + gpio_pin_config(GPIO_PA10, GPIO_MODE_IN_FLOAT); + + /* reset usart0 */ + RCU->APB2RST |= RCU_APB2RST_USART0RST; + RCU->APB2RST &= ~RCU_APB2RST_USART0RST; + + /* set baudrate */ + USART0->BAUD = (2 * pclk + target) / (2 * target); + /* set 1 stop bit */ + USART0->CTL1 = USART_CTL1_STB_1; + /* enable rx and tx */ + USART0->CTL0 = USART_CTL0_TEN | USART_CTL0_REN; + /* enable usart0 */ + USART0->CTL0 |= USART_CTL0_UEN; +} diff --git a/ports/gd32vf103/uart0.h b/ports/gd32vf103/uart0.h new file mode 100644 index 0000000000000..9fbe58c2e52c4 --- /dev/null +++ b/ports/gd32vf103/uart0.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 UART0_H +#define UART0_H + +#include + +void uart0_init(uint32_t pclk, uint32_t target); + +#endif diff --git a/ports/gd32vf103/usbacm.c b/ports/gd32vf103/usbacm.c new file mode 100644 index 0000000000000..11cede3d25b86 --- /dev/null +++ b/ports/gd32vf103/usbacm.c @@ -0,0 +1,1296 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 +#include +#include +#include + +#include "gd32vf103/rcu.h" +#include "gd32vf103/mtimer.h" +#include "gd32vf103/usbfs.h" + +#include "lib/eclic.h" + +#include "py/mpconfig.h" + +#include "usbacm.h" + +#if 1 +#define debug(...) +#else +#include "uart0.h" +#define debug(...) uart0_printf(__VA_ARGS__) +#endif + +#define CDC_INTERFACE 0 +#define CDC_ENDPOINT 2 +/* up to 64 bytes for full-speed interrupt eps */ +#define CDC_PACKETSIZE 8 + +#define ACM_INTERFACE 1 +#define ACM_ENDPOINT 1 +/* 8, 16, 32 or 64 bytes for full-speed bulk eps */ +#define ACM_PACKETSIZE 64 + +#define USBFS_FIFO_RXSIZE 512 +#define USBFS_FIFO_TX0SIZE 256 +#define USBFS_FIFO_TX1SIZE 256 +#define USBFS_FIFO_TX2SIZE 64 +#define USBFS_FIFO_TX3SIZE 0 + +#define USB_WORD(x) ((x) & 0xFF),((x) >> 8) +#define USB_TRIPLE(x) ((x) & 0xFF),(((x) >> 8) & 0xFF),((x) >> 16) +#define USB_QUAD(x) ((x) & 0xFF),(((x) >> 8) & 0xFF),(((x) >> 16) & 0xFF),((x) >> 24) + +struct usb_setup_packet { + union { + struct { + uint8_t bmRequestType; + uint8_t bRequest; + }; + uint16_t request; + }; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +struct usb_descriptor_device { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +}; + +struct usb_descriptor_device_qualifier { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + uint8_t bReserved; +}; + +struct usb_descriptor_configuration { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; + uint8_t rest[]; +}; + +struct usb_descriptor_string { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wCodepoint[]; +}; + +struct usb_setup_handler { + uint16_t req; + uint8_t idx; + uint8_t len; + int (*fn)(const struct usb_setup_packet *p, const void **data); +}; + + +struct acm_line_coding { + uint32_t dwDTERate; + uint8_t bCharFormat; + uint8_t bParityType; + uint8_t bDataBits; +}; + +static const struct usb_descriptor_device usbfs_descriptor_device = { + .bLength = 18, + .bDescriptorType = 0x01, /* Device */ + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, /* 0x00 = per interface */ + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = 64, + .idVendor = 0x1d50, /* OpenMoko vendor id */ + .idProduct = 0x613f, /* GeckoBoot target product id */ + .bcdDevice = 0x0200, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +static const struct usb_descriptor_configuration usbfs_descriptor_configuration1 = { + .bLength = 9, + .bDescriptorType = 0x02, /* Configuration */ + .wTotalLength = 9 + 8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7, + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 250, + .rest = { + /* Interface Association */ + /* .bLength */ 8, + /* .bDescriptorType */ 0x0B, /* Interface Association */ + /* .bFirstInterface */ CDC_INTERFACE, + /* .bInterfaceCount */ 2, + /* .bFunctionClass */ 0x02, /* 0x02 = CDC */ + /* .bFunctionSubClass */ 0x02, /* 0x02 = ACM */ + /* .bFunctionProtocol */ 0, + /* .iFunction */ 0, + /* Interface */ + /* .bLength */ 9, + /* .bDescriptorType */ 0x04, /* Interface */ + /* .bInterfaceNumber */ CDC_INTERFACE, + /* .bAlternateSetting */ 0, + /* .bNumEndpoints */ 1, + /* .bInterfaceClass */ 0x02, /* 0x02 = CDC */ + /* .bInterfaceSubClass */ 0x02, /* 0x02 = ACM */ + /* .bInterfaceProtocol */ 0x01, /* 0x00 = no protocol required, 0x01 = AT commands V.250 etc */ + /* .iInterface */ 0, + /* CDC Header */ + /* .bLength */ 5, + /* .bDescriptorType */ 0x24, /* CS_INTERFACE */ + /* .bDescriptorSubtype */ 0x00, /* Header */ + /* .bcdCDC */ USB_WORD(0x0120), + /* CDC Call Management */ + /* .bLength */ 5, + /* .bDescriptorType */ 0x24, /* CS_INTERFACE */ + /* .bDescriptorSubtype */ 0x01, /* Call Management */ + /* .bmCapabilities */ 0x03, /* Handles call management over data line */ + /* .bDataInterface */ ACM_INTERFACE, + /* CDC ACM */ + /* .bLength */ 4, + /* .bDescriptorType */ 0x24, /* CS_INTERFACE */ + /* .bDescriptorSubtype */ 0x02, /* ACM */ + /* .bmCapabilities */ 0x02, /* 0x02 = supports line state and coding */ + /* CDC Union */ + /* .bLength */ 5, + /* .bDescriptorType */ 0x24, /* CS_INTERFACE */ + /* .bDescriptorSubtype */ 0x06, /* Union */ + /* .bControlInterface */ CDC_INTERFACE, + /* .bSubordinateInterface0 */ ACM_INTERFACE, + /* Endpoint */ + /* .bLength */ 7, + /* .bDescriptorType */ 0x05, /* Endpoint */ + /* .bEndpointAddress */ 0x80 | CDC_ENDPOINT, /* in */ + /* .bmAttributes */ 0x03, /* interrupt */ + /* .wMaxPacketSize */ USB_WORD(CDC_PACKETSIZE), + /* .bInterval */ 255, /* poll every 255ms */ + /* Interface */ + /* .bLength */ 9, + /* .bDescriptorType */ 0x04, /* Interface */ + /* .bInterfaceNumber */ ACM_INTERFACE, + /* .bAlternateSetting */ 0, + /* .bNumEndpoints */ 2, + /* .bInterfaceClass */ 0x0A, /* 0x0A = CDC Data */ + /* .bInterfaceSubClass */ 0x00, + /* .bInterfaceProtocol */ 0x00, + /* .iInterface */ 0, + /* Endpoint */ + /* .bLength */ 7, + /* .bDescriptorType */ 0x05, /* Endpoint */ + /* .bEndpointAddress */ 0x80 | ACM_ENDPOINT, /* in */ + /* .bmAttributes */ 0x02, /* bulk */ + /* .wMaxPacketSize */ USB_WORD(ACM_PACKETSIZE), + /* .bInterval */ 0, /* unused */ + /* Endpoint */ + /* .bLength */ 7, + /* .bDescriptorType */ 0x05, /* Endpoint */ + /* .bEndpointAddress */ ACM_ENDPOINT, /* out */ + /* .bmAttributes */ 0x02, /* bulk */ + /* .wMaxPacketSize */ USB_WORD(ACM_PACKETSIZE), + /* .bInterval */ 0, /* unused */ + } +}; + +static const struct usb_descriptor_string usbfs_descriptor_string0 = { + .bLength = 4, + .bDescriptorType = 0x03, /* String */ + .wCodepoint = { + 0x0409, /* English (US) */ + }, +}; + +static const struct usb_descriptor_string usbfs_descriptor_manufacturer = { + .bLength = 16, + .bDescriptorType = 0x03, /* String */ + .wCodepoint = { + 'L','a','b','i','t','a','t', + }, +}; + +static const struct usb_descriptor_string usbfs_descriptor_product = { + .bLength = 20, + .bDescriptorType = 0x03, /* String */ + .wCodepoint = { + 'G','D','3','2','V','F','1','0','3', + }, +}; + +/* must be at least 12 characters long and consist of only '0'-'9','A'-'B' + * at least according to the mass-storage bulk-only document */ +static const struct usb_descriptor_string usbfs_descriptor_serial = { + .bLength = 26, + .bDescriptorType = 0x03, /* String */ + .wCodepoint = { + '0','0','0','0','0','0','0','0','0','0','0','1', + }, +}; + +static const struct usb_descriptor_string *const usbfs_descriptor_string[] = { + &usbfs_descriptor_string0, + &usbfs_descriptor_manufacturer, + &usbfs_descriptor_product, + &usbfs_descriptor_serial, +}; + +static struct { + uint32_t *ep0out; + const unsigned char *ep0in; + uint32_t bytes; + uint32_t packetsize; +} usbfs_state; + +static struct { + union { + struct usb_setup_packet setup; + uint32_t v[2]; + }; + uint8_t data[64]; +} usbfs_outbuf; + +static uint16_t usbfs_status; + +static struct acm_line_coding acm_line_coding; + +static volatile bool acm_inidle; +static volatile uint16_t acm_inhead; +static volatile uint16_t acm_intail; +static uint8_t acm_inbuf[1024]; + +static volatile uint8_t acm_outbytes; +static union { + uint32_t word[64 / 4]; + uint8_t byte[64]; +} acm_outbuf; + +int mp_hal_stdin_rx_chr(void) { + static uint8_t head; + unsigned int bytes; + int ret; + + #if 0 + while (1) { + eclic_global_interrupt_disable(); + bytes = acm_outbytes; + if (bytes > 0) { + break; + } + wait_for_interrupt(); + eclic_global_interrupt_enable(); + } + eclic_global_interrupt_enable(); + #else + do { + bytes = acm_outbytes; + } while (bytes == 0); + #endif + + ret = acm_outbuf.byte[head]; + acm_outbytes = --bytes; + if (bytes == 0) { + head = 0; + USBFS->DOEP[ACM_ENDPOINT].LEN = USBFS_DOEPLEN_PCNT(1) | ACM_PACKETSIZE; + USBFS->DOEP[ACM_ENDPOINT].CTL |= USBFS_DOEPCTL_EPEN | USBFS_DOEPCTL_CNAK; + } else { + head++; + } + + return ret; +} + +static void +acm_send(void) { + unsigned int head = acm_inhead; + unsigned int tail = acm_intail; + unsigned int len; + uint32_t dieplen; + + if (head == tail) { + acm_inidle = true; + return; + } + + len = (tail - head) % ARRAY_SIZE(acm_inbuf); + debug("tfstat%x=%lu, len=%u\n", ACM_ENDPOINT, + USBFS->DIEP[ACM_ENDPOINT].TFSTAT, + len); + if (len >= 4 * ACM_PACKETSIZE) { + len = 4 * ACM_PACKETSIZE; + dieplen = USBFS_DIEPLEN_PCNT(4U); + } else if (len >= 3 * ACM_PACKETSIZE) { + len = 3 * ACM_PACKETSIZE; + dieplen = USBFS_DIEPLEN_PCNT(3U); + } else if (len >= 2 * ACM_PACKETSIZE) { + len = 2 * ACM_PACKETSIZE; + dieplen = USBFS_DIEPLEN_PCNT(2U); + } else { + if (len > ACM_PACKETSIZE) { + len = ACM_PACKETSIZE; + } + dieplen = USBFS_DIEPLEN_PCNT(1U); + } + dieplen |= len; + + tail = (head + len) % ARRAY_SIZE(acm_inbuf); + acm_inhead = tail; + + USBFS->DIEP[ACM_ENDPOINT].LEN = dieplen; + USBFS->DIEP[ACM_ENDPOINT].CTL |= USBFS_DIEPCTL_EPEN | USBFS_DIEPCTL_CNAK; + do { + uint32_t v = acm_inbuf[head++]; + + head %= ARRAY_SIZE(acm_inbuf); + if (head != tail) { + v |= ((uint32_t)acm_inbuf[head++]) << 8; + head %= ARRAY_SIZE(acm_inbuf); + } + if (head != tail) { + v |= ((uint32_t)acm_inbuf[head++]) << 16; + head %= ARRAY_SIZE(acm_inbuf); + } + if (head != tail) { + v |= ((uint32_t)acm_inbuf[head++]) << 24; + head %= ARRAY_SIZE(acm_inbuf); + } + USBFS->DFIFO[ACM_ENDPOINT][0] = v; + } while (head != tail); +} + +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + unsigned int tail = acm_intail; + + for (; len > 0; len--, str++) { + acm_inbuf[tail++] = *str; + tail %= ARRAY_SIZE(acm_inbuf); + while (tail == acm_inhead) { + /* wait */ + } + } + + acm_intail = tail; + if (acm_inidle) { + acm_inidle = false; + acm_send(); + } +} + +static void +usbfs_udelay(unsigned int us) { + uint32_t zero = MTIMER->mtime_lo; + uint32_t end = us * (CORECLOCK / 4000000) + 1; + uint32_t now; + + do { + now = MTIMER->mtime_lo - zero; + } while (now < end); +} + +static void +usbfs_ep0in_transfer(void) { + uint32_t len = usbfs_state.bytes; + const unsigned char *p = usbfs_state.ep0in; + const unsigned char *end; + + if (len > usbfs_state.packetsize) { + len = usbfs_state.packetsize; + } + + end = p + len; + + debug("tfstat%x=%lu (0x%03x)\n", 0, + USBFS->DIEP[0].TFSTAT, + (uintptr_t)&USBFS->DIEP[0].TFSTAT - (uintptr_t)USBFS); + + USBFS->DIEP[0].LEN = USBFS_DIEPLEN_PCNT(1U) | len; + USBFS->DIEP[0].CTL |= USBFS_DIEPCTL_EPEN | USBFS_DIEPCTL_CNAK; + while (p < end) { + uint32_t v = *p++; + if (p < end) { + v |= (*p++) << 8; + } + if (p < end) { + v |= (*p++) << 16; + } + if (p < end) { + v |= (*p++) << 24; + } + USBFS->DFIFO[0][0] = v; + } +} + +#if 1 +static void +usbfs_ep0in_transfer_empty(void) { + USBFS->DIEP[0].LEN = USBFS_DIEPLEN_PCNT(1U); + USBFS->DIEP[0].CTL |= USBFS_DIEPCTL_EPEN | USBFS_DIEPCTL_CNAK; +} +#else +static inline void +usbfs_ep0in_transfer_empty(void) { + usbfs_ep0in_transfer(); +} +#endif + +static inline void +usbfs_ep0in_stall(void) { + USBFS->DIEP[0].CTL |= USBFS_DIEPCTL_STALL; +} + +static void +usbfs_ep0out_prepare_setup(void) { + USBFS->DOEP[0].LEN = + USBFS_DOEPLEN_STPCNT(3U) | + USBFS_DOEPLEN_PCNT(0U) | + USBFS_DOEPLEN_TLEN(0U); + USBFS->DOEP[0].CTL |= USBFS_DOEPCTL_EPEN | USBFS_DOEPCTL_STALL; +} + +static void +usbfs_ep0out_prepare_out(void) { + USBFS->DOEP[0].LEN = + USBFS_DOEPLEN_STPCNT(3U) | + USBFS_DOEPLEN_PCNT(1U) | + USBFS_DOEPLEN_TLEN(usbfs_state.packetsize); + USBFS->DOEP[0].CTL |= USBFS_DOEPCTL_EPEN | USBFS_DOEPCTL_CNAK; +} + +static void +usbfs_suspend(void) { + /* + usbfs_phy_stop(); + usbfs_flags_enable(USBFS_GINTMSK_WUIM + | USBFS_GINTMSK_USBRST); + */ +} + +static void +usbfs_wakeup(void) { + /* + usbfs_phy_start(); + usbfs_flags_enable(USB_OTG_GINTMSK_ENUMDNEM + | USB_OTG_GINTMSK_USBRST + | USB_OTG_GINTMSK_USBSUSPM + | USB_OTG_GINTMSK_OEPINT + | USB_OTG_GINTMSK_IEPINT); + */ +} + +static void +usbfs_txfifos_flush(void) { + /* flush all tx fifos */ + USBFS->GRSTCTL |= USBFS_GRSTCTL_TXFNUM(0x10U) | USBFS_GRSTCTL_TXFF; + while ((USBFS->GRSTCTL & USBFS_GRSTCTL_TXFF)) { + /* wait */ + } + /* wait 3 more phy clocks */ + usbfs_udelay(3); +} + +static void +usbfs_ep_reset(void) { + unsigned int i; + + USBFS->DIEP[0].CTL = + USBFS_DIEPCTL_STALL | + USBFS_DIEPCTL_EPTYPE_CONTROL | + USBFS_DIEP0CTL_MPL_64B; + USBFS->DIEP[0].INTF = + USBFS_DIEPINTF_IEPNE | + USBFS_DIEPINTF_EPTXFUD | + USBFS_DIEPINTF_CITO | + USBFS_DIEPINTF_EPDIS | + USBFS_DIEPINTF_TF; + for (i = 1; i < 4; i++) { + /* + if (USBFS->DIEP[i].CTL & USBFS_DIEPCTL_EPEN) + USBFS->DIEP[i].CTL = USBFS_DIEPCTL_EPD | USBFS_DIEPCTL_SNAK; + else + */ + USBFS->DIEP[i].CTL = USBFS_DIEPCTL_SNAK; + USBFS->DIEP[i].INTF = + USBFS_DIEPINTF_IEPNE | + USBFS_DIEPINTF_EPTXFUD | + USBFS_DIEPINTF_CITO | + USBFS_DIEPINTF_EPDIS | + USBFS_DIEPINTF_TF; + USBFS->DIEP[i].LEN = 0; + } + + USBFS->DOEP[0].CTL = + USBFS_DOEPCTL_STALL | + USBFS_DOEPCTL_EPTYPE_CONTROL | + USBFS_DOEP0CTL_MPL_64B; + USBFS->DOEP[0].INTF = + USBFS_DOEPINTF_BTBSTP | + USBFS_DOEPINTF_EPRXFOVR | + USBFS_DOEPINTF_STPF | + USBFS_DOEPINTF_EPDIS | + USBFS_DOEPINTF_TF; + for (i = 1; i < 4; i++) { + /* + if (USBFS->DOEP[i].CTL & USBFS_DOEPCTL_EPEN) + USBFS->DOEP[i].CTL = USBFS_DOEPCTL_EPD | USBFS_DOEPCTL_SNAK; + else + */ + USBFS->DOEP[i].CTL = USBFS_DOEPCTL_SNAK; + USBFS->DOEP[i].INTF = + USBFS_DOEPINTF_BTBSTP | + USBFS_DOEPINTF_EPRXFOVR | + USBFS_DOEPINTF_STPF | + USBFS_DOEPINTF_EPDIS | + USBFS_DOEPINTF_TF; + USBFS->DOEP[i].LEN = 0; + } +} + +static void +usbfs_reset(void) { + /* clear the remote wakeup signaling */ + USBFS->DCTL &= ~USBFS_DCTL_RWKUP; + + /* flush all tx fifos */ + usbfs_txfifos_flush(); + + /* reset endpoint registers */ + usbfs_ep_reset(); + + /* reset address */ + USBFS->DCFG &= ~USBFS_DCFG_DAR_Msk; + + /* enable interrupts for endpoint 0 only */ + USBFS->DAEPINTEN = + USBFS_DAEPINTEN_OEPIE(1U) | + USBFS_DAEPINTEN_IEPIE(1U); + USBFS->DOEPINTEN = + USBFS_DOEPINTEN_STPFEN | + /* USBFS_DOEPINTEN_EPDISEN | */ + USBFS_DOEPINTEN_TFEN; + USBFS->DIEPINTEN = + /* USBFS_DIEPINTEN_CITOEN | */ + /* USBFS_DIEPINTEN_EPDISEN | */ + USBFS_DIEPINTEN_TFEN; + + /* reset internal state */ + usbfs_state.bytes = 0; + usbfs_state.packetsize = 64; +} + +static void +usbfs_enumdone(void) { + USBFS->DCTL |= USBFS_DCTL_CGINAK; + + if ((USBFS->DSTAT & USBFS_DSTAT_ES_Msk) == USBFS_DSTAT_ES_FULL) { + /* we already set 64 byte packages at reset */ + debug("full speed.. "); + } else { + /* use 8 byte packages */ + USBFS->DIEP[0].CTL |= USBFS_DIEP0CTL_MPL_8B; + USBFS->DOEP[0].CTL |= USBFS_DOEP0CTL_MPL_8B; + usbfs_state.packetsize = 8; + debug("low speed.. "); + } + + /* prepare to receive setup package */ + usbfs_ep0out_prepare_setup(); +} + +static int +usbfs_handle_get_status_device(const struct usb_setup_packet *p, const void **data) { + debug("GET_STATUS: device\n"); + *data = &usbfs_status; + return 2; +} + +static int +usbfs_handle_set_address(const struct usb_setup_packet *p, const void **data) { + debug("SET_ADDRESS: wValue = %hu\n", p->wValue); + USBFS->DCFG = (USBFS->DCFG & ~USBFS_DCFG_DAR_Msk) | + USBFS_DCFG_DAR((uint32_t)p->wValue); + return 0; +} + +static int +usbfs_handle_get_descriptor_device(const void **data, uint8_t index) { + if (index != 0) { + debug("GET_DESCRIPTOR: type = 0x01, but index = 0x%02x\n", index); + return -1; + } + *data = &usbfs_descriptor_device; + return sizeof(usbfs_descriptor_device); +} + +static int +usbfs_handle_get_descriptor_configuration(const void **data, uint8_t index) { + if (index != 0) { + debug("GET_DESCRIPTOR: unknown configuration %hu\n", index); + return -1; + } + *data = &usbfs_descriptor_configuration1; + return usbfs_descriptor_configuration1.wTotalLength; +} + +static int +usbfs_handle_get_descriptor_string(const void **data, uint8_t index) { + const struct usb_descriptor_string *desc; + + if (index >= ARRAY_SIZE(usbfs_descriptor_string)) { + debug("GET_DESCRIPTOR: unknown string %hu\n", index); + return -1; + } + desc = usbfs_descriptor_string[index]; + *data = desc; + return desc->bLength; +} + +static int +usbfs_handle_get_descriptor(const struct usb_setup_packet *p, const void **data) { + uint8_t type = p->wValue >> 8; + uint8_t index = p->wValue & 0xFFU; + + switch (type) { + case 0x01: + debug("GET_DESCRIPTOR: device, %u bytes\n", p->wLength); + return usbfs_handle_get_descriptor_device(data, index); + case 0x02: + debug("GET_DESCRIPTOR: configuration %u, %u bytes\n", + index, p->wLength); + return usbfs_handle_get_descriptor_configuration(data, index); + case 0x03: + debug("GET_DESCRIPTOR: string %u, %u bytes\n", + index, p->wLength); + return usbfs_handle_get_descriptor_string(data, index); + #ifndef NDEBUG + case 0x06: /* DEVICE QUALIFIER (for high-speed) */ + debug("DEVICE_QUALIFIER\n"); + break; + default: + debug("GET_DESCRIPTOR: unknown type 0x%02x\n", type); + break; + #endif + } + return -1; +} + +static int +usbfs_handle_get_configuration(const struct usb_setup_packet *p, const void **data) { + debug("GET_CONFIGURATION\n"); + *data = &usbfs_descriptor_configuration1.bConfigurationValue; + return 1; +} + +static int +usbfs_handle_set_configuration(const struct usb_setup_packet *p, const void **data) { + debug("SET_CONFIGURATION: wValue = %hu\n", p->wValue); + + if (p->wValue != usbfs_descriptor_configuration1.bConfigurationValue) { + return -1; + } + + /* configure CDC endpoint */ + USBFS->DIEP[CDC_ENDPOINT].CTL = + USBFS_DIEPCTL_SNAK | + USBFS_DIEPCTL_EPTYPE_INTERRUPT | + USBFS_DIEPCTL_EPACT | + CDC_PACKETSIZE; + + /* configure ACM endpoints */ + USBFS->DIEP[ACM_ENDPOINT].CTL = + USBFS_DIEPCTL_SNAK | + USBFS_DIEPCTL_EPTYPE_BULK | + USBFS_DIEPCTL_EPACT | + ACM_PACKETSIZE; + + USBFS->DOEP[ACM_ENDPOINT].LEN = USBFS_DOEPLEN_PCNT(1) | ACM_PACKETSIZE; + USBFS->DOEP[ACM_ENDPOINT].CTL = + USBFS_DOEPCTL_EPEN | + USBFS_DOEPCTL_CNAK | + USBFS_DOEPCTL_EPTYPE_BULK | + USBFS_DOEPCTL_EPACT | + ACM_PACKETSIZE; + + USBFS->DAEPINTEN |= + /* (1U << (CDC_ENDPOINT + USBFS_DAEPINTEN_IEPIE_Pos)) | */ + (1U << (ACM_ENDPOINT + USBFS_DAEPINTEN_IEPIE_Pos)) | + (1U << (ACM_ENDPOINT + USBFS_DAEPINTEN_OEPIE_Pos)); + + acm_line_coding.dwDTERate = 9600; + acm_line_coding.bCharFormat = 0; + acm_line_coding.bParityType = 0; + acm_line_coding.bDataBits = 8; + acm_inidle = true; + acm_send(); + + return 0; +} + +static int +usbfs_handle_set_interface0(const struct usb_setup_packet *p, const void **data) { + debug("SET_INTERFACE: wIndex = %hu, wValue = %hu\n", p->wIndex, p->wValue); + + if (p->wValue != 0) { + return -1; + } + + return 0; +} + +static int +usbfs_handle_clear_feature_endpoint(const struct usb_setup_packet *p, const void **data) { + debug("CLEAR_FEATURE endpoint %hu\n", p->wIndex); + return -1; +} + +static int +acm_set_control_line_state(const struct usb_setup_packet *p, const void **data) { + debug("SET_CONTROL_LINE_STATE: wIndex = %hu wValue = 0x%04hx\r\n", + p->wIndex, p->wValue); + + debug(" RTS %u DTR %u\r\n", !!(p->wValue & 0x2), !!(p->wValue & 0x1)); + return 0; +} + +static int +acm_set_line_coding(const struct usb_setup_packet *p, const void **data) { + const struct acm_line_coding *lc = *data; + + debug("SET_LINE_CODING: {\r\n" + " dwDTERate = %lu\r\n" + " bCharFormat = 0x%02x\r\n" + " bParityType = 0x%02x\r\n" + " bDataBits = 0x%02x\r\n" + "}\r\n", + lc->dwDTERate, + lc->bCharFormat, + lc->bParityType, + lc->bDataBits); + + acm_line_coding.dwDTERate = lc->dwDTERate; + acm_line_coding.bCharFormat = lc->bCharFormat; + acm_line_coding.bParityType = lc->bParityType; + acm_line_coding.bDataBits = lc->bDataBits; + + debug("GRFLEN = 0x%08lx\n", USBFS->GRFLEN); + debug("DIEP0TFLEN = 0x%08lx\n", USBFS->DIEP0TFLEN); + debug("DIEP1TFLEN = 0x%08lx\n", USBFS->DIEP1TFLEN); + debug("DIEP2TFLEN = 0x%08lx\n", USBFS->DIEP2TFLEN); + debug("DIEP3TFLEN = 0x%08lx\n", USBFS->DIEP3TFLEN); + return 0; +} + +static int +acm_get_line_coding(const struct usb_setup_packet *p, const void **data) { + debug("GET_LINE_CODING: {\r\n" + " dwDTERate = %lu\r\n" + " bCharFormat = 0x%02x\r\n" + " bParityType = 0x%02x\r\n" + " bDataBits = 0x%02x\r\n" + "}\r\n", + acm_line_coding.dwDTERate, + acm_line_coding.bCharFormat, + acm_line_coding.bParityType, + acm_line_coding.bDataBits); + *data = &acm_line_coding; + return 7; +} + +static const struct usb_setup_handler usbfs_setup_handlers[] = { + { .req = 0x0080, .idx = 0, .len = -1, .fn = usbfs_handle_get_status_device }, + { .req = 0x0500, .idx = 0, .len = 0, .fn = usbfs_handle_set_address }, + { .req = 0x0680, .idx = -1, .len = -1, .fn = usbfs_handle_get_descriptor }, + { .req = 0x0880, .idx = 0, .len = -1, .fn = usbfs_handle_get_configuration }, + { .req = 0x0900, .idx = 0, .len = 0, .fn = usbfs_handle_set_configuration }, + { .req = 0x0102, .idx = 0, .len = 0, .fn = usbfs_handle_clear_feature_endpoint }, + { .req = 0x0b01, .idx = CDC_INTERFACE, .len = 0, .fn = usbfs_handle_set_interface0 }, + { .req = 0x2221, .idx = CDC_INTERFACE, .len = 0, .fn = acm_set_control_line_state }, + { .req = 0x2021, .idx = CDC_INTERFACE, .len = 7, .fn = acm_set_line_coding }, + { .req = 0x21a1, .idx = CDC_INTERFACE, .len = 7, .fn = acm_get_line_coding }, + { .req = 0x0102, .idx = CDC_ENDPOINT, .len = 0, .fn = usbfs_handle_clear_feature_endpoint }, + { .req = 0x0b01, .idx = ACM_INTERFACE, .len = 0, .fn = usbfs_handle_set_interface0 }, + { .req = 0x0102, .idx = ACM_ENDPOINT, .len = 0, .fn = usbfs_handle_clear_feature_endpoint }, +}; + +static int +usbfs_setup_handler_run(const struct usb_setup_packet *p, const void **data) { + uint8_t idx = p->wIndex; + const struct usb_setup_handler *h; + + for (h = usbfs_setup_handlers; h < ARRAY_END(usbfs_setup_handlers); h++) { + if (h->req == p->request && (h->idx == 0xFFU || h->idx == idx)) { + if (h->len != 0xFFU && h->len != p->wLength) { + break; + } + return h->fn(p, data); + } + } + + debug("unknown request:\n" + " bmRequestType 0x%02x\n" + " bRequest 0x%02x\n" + " wValue 0x%04x\n" + " wIndex 0x%04x\n" + " wLength 0x%04x\n", + p->bmRequestType, + p->bRequest, + p->wValue, + p->wIndex, + p->wLength); + return -1; +} + +static void +usbfs_handle_setup(void) { + const struct usb_setup_packet *p = &usbfs_outbuf.setup; + + usbfs_state.bytes = 0; + + if (p->bmRequestType & 0x80U) { + const void *data; + int ret = usbfs_setup_handler_run(p, &data); + + if (ret >= 0) { + /* send IN data */ + if (ret > p->wLength) { + ret = p->wLength; + } + usbfs_state.ep0in = data; + usbfs_state.bytes = ret; + usbfs_ep0in_transfer(); + /* prepare for IN ack */ + usbfs_ep0out_prepare_out(); + return; + } + } else if (p->wLength == 0) { + const void *data; + + if (!usbfs_setup_handler_run(p, &data)) { + /* send empty ack package */ + usbfs_ep0in_transfer_empty(); + /* prepare for next SETUP package */ + usbfs_ep0out_prepare_setup(); + return; + } + } else if (p->wLength <= ARRAY_SIZE(usbfs_outbuf.data)) { + /* receive OUT data */ + usbfs_ep0out_prepare_out(); + usbfs_state.bytes = p->wLength; + return; + } + + /* stall IN endpoint */ + usbfs_ep0in_stall(); + /* prepare for next SETUP package */ + usbfs_ep0out_prepare_setup(); +} + +static void +usbfs_handle_rx0(bool setup, unsigned int len) { + if (setup) { + for (; len > 8; len -= 4) { + (void)USBFS->DFIFO[0][0]; + } + usbfs_state.ep0out = usbfs_outbuf.v; + *usbfs_state.ep0out++ = USBFS->DFIFO[0][0]; + *usbfs_state.ep0out++ = USBFS->DFIFO[0][0]; + } else { + while (1) { + *usbfs_state.ep0out++ = USBFS->DFIFO[0][0]; + if (len <= 4) { + break; + } + len -= 4; + } + } +} + +static void +usbfs_handle_ep0(void) { + uint32_t oflags = USBFS->DOEP[0].INTF; + uint32_t iflags = USBFS->DIEP[0].INTF; + uint32_t bytes; + + USBFS->DOEP[0].INTF = oflags; + USBFS->DIEP[0].INTF = iflags; + + // debug("EP0 %04lx %04lx %lu\n", oflags, iflags, usbfs_state.bytes); + + if (oflags & USBFS_DOEPINTF_STPF) { + usbfs_handle_setup(); + return; + } + + bytes = usbfs_state.bytes; + if (bytes == 0) { + return; + } + + if (iflags & USBFS_DIEPINTF_TF) { + /* data IN */ + if (bytes > usbfs_state.packetsize) { + /* send next package */ + usbfs_state.ep0in += usbfs_state.packetsize; + usbfs_state.bytes = bytes - usbfs_state.packetsize; + usbfs_ep0in_transfer(); + } else { + usbfs_state.bytes = 0; + } + } else if (oflags & USBFS_DOEPINTF_TF) { + /* data OUT */ + bytes = usbfs_state.packetsize - (USBFS->DOEP[0].LEN & USBFS_DOEPLEN_TLEN_Msk); + if (usbfs_state.bytes > bytes) { + usbfs_state.bytes -= bytes; + /* prepare for more OUT data */ + usbfs_ep0out_prepare_out(); + } else { + const void *data = usbfs_outbuf.data; + + usbfs_state.bytes = 0; + if (!usbfs_setup_handler_run(&usbfs_outbuf.setup, &data)) { + /* send empty ack package */ + usbfs_ep0in_transfer_empty(); + } else { + usbfs_ep0in_stall(); + } + usbfs_ep0out_prepare_setup(); + } + } +} + +static void +acm_handle_rx(bool setup, unsigned int len) { + for (uint32_t *p = acm_outbuf.word;; p++) { + *p = USBFS->DFIFO[ACM_ENDPOINT][0]; + if (len <= 4) { + break; + } + len -= 4; + } +} + +static void +acm_handle_ep(void) { + uint32_t flags = USBFS->DOEP[ACM_ENDPOINT].INTF; + + USBFS->DOEP[ACM_ENDPOINT].INTF = flags; + + /* + debug("ACM OUT: 0x%02lx (0x%08lx)\n", flags, + USBFS->DOEP[ACM_ENDPOINT].CTL); + */ + + if (flags & USBFS_DOEPINTF_TF) { + unsigned int bytes = ACM_PACKETSIZE - + (USBFS->DOEP[ACM_ENDPOINT].LEN & USBFS_DOEPLEN_TLEN_Msk); + + acm_outbytes = bytes; + } + + flags = USBFS->DIEP[ACM_ENDPOINT].INTF; + + USBFS->DIEP[ACM_ENDPOINT].INTF = flags; + + /* + debug("ACM IN: 0x%02lx (0x%08lx)\n", flags, + USBFS->DIEP[ACM_ENDPOINT].CTL); + */ + + if (flags & USBFS_DIEPINTF_TF) { + acm_send(); + } +} + +struct { + void (*rx)(bool setup, unsigned int len); + void (*ep)(void); +} usbfs_endpoints[] = { + { + .rx = usbfs_handle_rx0, + .ep = usbfs_handle_ep0, + }, + { + .rx = acm_handle_rx, + .ep = acm_handle_ep, + }, +}; + +static void +usbfs_handle_rxdata(void) { + uint32_t grstat = USBFS->GRSTATP; + unsigned int len = (grstat & USBFS_GRSTAT_BCOUNT_Msk) >> USBFS_GRSTAT_BCOUNT_Pos; + unsigned int ep; + bool setup; + + if (len == 0) { + return; + } + + ep = grstat & USBFS_GRSTAT_EPNUM_Msk; + setup = (grstat & USBFS_GRSTAT_RPCKST_Msk) == USBFS_GRSTAT_RPCKST_STP; + usbfs_endpoints[ep].rx(setup, len); +} + +static void +usbfs_handle_endpoints(void) { + uint32_t flags = USBFS->DAEPINT; + uint32_t mask = 0x10001U; + + for (unsigned int i = 0; i < ARRAY_SIZE(usbfs_endpoints); i++, mask <<= 1) { + if (flags & mask) { + usbfs_endpoints[i].ep(); + } + } +} + +void +USBFS_IRQHandler(void) { + uint32_t flags = USBFS->GINTF; + + // debug("flags = %08lx\n", flags); + + /* read all incoming packets */ + while ((flags & USBFS_GINTF_RXFNEIF)) { + usbfs_handle_rxdata(); + flags = USBFS->GINTF; + } + + if (flags & (USBFS_GINTF_OEPIF | USBFS_GINTF_IEPIF)) { + usbfs_handle_endpoints(); + } + + /* + if (!(flags & ( + USBFS_GINTF_SP | + USBFS_GINTF_WKUPIF | + USBFS_GINTF_RST | + USBFS_GINTF_ENUMF))) + return; + */ + + if (flags & USBFS_GINTF_SP) { + debug("SUSPEND.. "); + usbfs_suspend(); + USBFS->GINTF = USBFS_GINTF_SP; + debug("done\n"); + return; + } + if (flags & USBFS_GINTF_WKUPIF) { + debug("WAKEUP.. "); + usbfs_wakeup(); + USBFS->GINTF = USBFS_GINTF_WKUPIF; + debug("done\n"); + } + if (flags & USBFS_GINTF_RST) { + debug("RESET.. "); + usbfs_reset(); + USBFS->GINTF = USBFS_GINTF_RST; + debug("done\n"); + } + if (flags & USBFS_GINTF_ENUMF) { + debug("ENUMDONE.. "); + usbfs_enumdone(); + USBFS->GINTF = USBFS_GINTF_ENUMF; + debug("done\n"); + } +} + +static void +usbfs_allocate_buffers(uint32_t rx, + uint32_t tx0, uint32_t tx1, uint32_t tx2, uint32_t tx3) { + /* round up to number of 32bit words */ + rx = (rx + 3) >> 2; + tx0 = (tx0 + 3) >> 2; + tx1 = (tx1 + 3) >> 2; + tx2 = (tx2 + 3) >> 2; + tx3 = (tx3 + 3) >> 2; + USBFS->GRFLEN = rx; + debug("GRFLEN (0x%03x) = %3lu = 0x%08lx\n", + (uintptr_t)&USBFS->GRFLEN - (uintptr_t)USBFS, + rx, + USBFS->GRFLEN); + USBFS->DIEP0TFLEN = (tx0 << 16) | rx; + debug("DIEP0TFLEN (0x%03x) = %3lu, %4lu = 0x%08lx\n", + (uintptr_t)&USBFS->DIEP0TFLEN - (uintptr_t)USBFS, + tx0, rx, + USBFS->DIEP0TFLEN); + USBFS->DIEP1TFLEN = (tx1 << 16) | (rx + tx0); + debug("DIEP1TFLEN (0x%03x) = %3lu, %4lu = 0x%08lx\n", + (uintptr_t)&USBFS->DIEP1TFLEN - (uintptr_t)USBFS, + tx1, (rx + tx0), + USBFS->DIEP1TFLEN); + USBFS->DIEP2TFLEN = (tx2 << 16) | (rx + tx0 + tx1); + debug("DIEP2TFLEN (0x%03x) = %3lu, %4lu = 0x%08lx\n", + (uintptr_t)&USBFS->DIEP2TFLEN - (uintptr_t)USBFS, + tx2, (rx + tx0 + tx1), + USBFS->DIEP2TFLEN); + USBFS->DIEP3TFLEN = (tx3 << 16) | (rx + tx0 + tx1 + tx2); + debug("DIEP3TFLEN (0x%03x) = %3lu, %4lu = 0x%08lx\n", + (uintptr_t)&USBFS->DIEP3TFLEN - (uintptr_t)USBFS, + tx3, (rx + tx0 + tx1 + tx2), + USBFS->DIEP3TFLEN); +} + +void +usbacm_init(void) { + /* turn on USBFS clock */ + RCU->AHBEN |= RCU_AHBEN_USBFSEN; + + /* turn on GPIOA and AFIO */ + RCU->APB2EN |= RCU_APB2EN_PAEN | RCU_APB2EN_AFEN; + + /* reset USBFS */ + RCU->AHBRST |= RCU_AHBRST_USBFSRST; + RCU->AHBRST &= ~RCU_AHBRST_USBFSRST; + + /* disable global interrupt flag */ + USBFS->GAHBCS = 0U; + + /* disable Vbus sensing */ + USBFS->GCCFG = USBFS_GCCFG_VBUSIG; + + debug("core reset"); + USBFS->GRSTCTL = USBFS_GRSTCTL_CSRST; + while ((USBFS->GRSTCTL & USBFS_GRSTCTL_CSRST)) { + debug("."); + } + debug(" done\n"); + usbfs_udelay(3); + + /* force device mode */ + debug("switching to device mode"); + USBFS->GUSBCS |= USBFS_GUSBCS_FDM; + while ((USBFS->GINTF & USBFS_GINTF_COPM)) { + debug("."); + } + debug(" done\n"); + + /* manual says: "the application must wait at + * least 25ms for [FDM to] take effect" */ + usbfs_udelay(25000); + + /* initialize device */ + USBFS->DCFG = + USBFS_DCFG_EOPFT_80PCT | + USBFS_DCFG_DS_FULL; + + /* disconnect */ + USBFS->DCTL = USBFS_DCTL_SD; + + /* now that we're disconnected, power on phy */ + USBFS->GCCFG = + USBFS_GCCFG_VBUSIG | + /* USBFS_GCCFG_VBUSACEN | */ + USBFS_GCCFG_VBUSBCEN | + USBFS_GCCFG_PWRON; + + /* setup fifo allocation */ + usbfs_allocate_buffers(USBFS_FIFO_RXSIZE, + USBFS_FIFO_TX0SIZE, + USBFS_FIFO_TX1SIZE, + USBFS_FIFO_TX2SIZE, + USBFS_FIFO_TX3SIZE); + + /* flush all tx fifos */ + usbfs_txfifos_flush(); + + /* flush rx fifo */ + USBFS->GRSTCTL |= USBFS_GRSTCTL_RXFF; + while ((USBFS->GRSTCTL & USBFS_GRSTCTL_RXFF)) { + /* wait */ + } + /* wait 3 more phy clocks */ + usbfs_udelay(3); + + USBFS->DIEPINTEN = 0U; + USBFS->DOEPINTEN = 0U; + USBFS->DAEPINTEN = 0U; + + /* reset endpoint registers */ + usbfs_ep_reset(); + + /* clear all sticky interrupts */ + USBFS->GINTF = + USBFS_GINTF_WKUPIF | + USBFS_GINTF_SESIF | + USBFS_GINTF_DISCIF | + USBFS_GINTF_IDPSC | + USBFS_GINTF_ISOONCIF | + USBFS_GINTF_ISOINCIF | + USBFS_GINTF_EOPFIF | + USBFS_GINTF_ISOOPDIF | + USBFS_GINTF_ENUMF | + USBFS_GINTF_RST | + USBFS_GINTF_SP | + USBFS_GINTF_ESP | + USBFS_GINTF_SOF | + USBFS_GINTF_MFIF; + + /* enable interrupts */ + USBFS->GINTEN = + USBFS_GINTEN_WKUPIE | + USBFS_GINTEN_OEPIE | + USBFS_GINTEN_IEPIE | + USBFS_GINTEN_ENUMFIE | + USBFS_GINTEN_RSTIE | + USBFS_GINTEN_RXFNEIE | + USBFS_GINTEN_SPIE; + + /* enable eclic interrupt */ + eclic_config(USBFS_IRQn, ECLIC_ATTR_TRIG_LEVEL, 4); + eclic_enable(USBFS_IRQn); + + /* set usb global interrupt flag */ + USBFS->GAHBCS |= USBFS_GAHBCS_GINTEN; + + /* connect */ + USBFS->DCTL &= ~USBFS_DCTL_SD; +} diff --git a/ports/gd32vf103/usbacm.h b/ports/gd32vf103/usbacm.h new file mode 100644 index 0000000000000..aa42ceb9cb37e --- /dev/null +++ b/ports/gd32vf103/usbacm.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 USBACM_H +#define USBACM_H + +void usbacm_init(void); + +#endif diff --git a/ports/stm32/led.c b/ports/stm32/led.c index adcb240cc0cc6..9458b2173425e 100644 --- a/ports/stm32/led.c +++ b/ports/stm32/led.c @@ -25,6 +25,7 @@ */ #include +#include #include "py/runtime.h" #include "py/mphal.h" diff --git a/ports/unix/Makefile b/ports/unix/Makefile index ff5f355022bc5..7380e5e41259c 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -39,7 +39,7 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wpointer-arith -Wuninitialized -Wdouble-promotion -Wsign-compare -Wfloat-conversion +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(CFLAGS_MOD) $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Debugging/Optimization diff --git a/ports/unix/main.c b/ports/unix/main.c index 0fe492a554cbd..6faeacb58672f 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "py/compile.h" #include "py/runtime.h" diff --git a/ports/unix/modtermios.c b/ports/unix/modtermios.c index 7a578becb9cc1..19ad276a93df0 100644 --- a/ports/unix/modtermios.c +++ b/ports/unix/modtermios.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "py/objlist.h" #include "py/runtime.h" diff --git a/ports/unix/moduselect.c b/ports/unix/moduselect.c index 6a0ee79aa6f0d..195e424dc5776 100644 --- a/ports/unix/moduselect.c +++ b/ports/unix/moduselect.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "py/runtime.h" #include "py/stream.h" diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 17f4895573ed5..5e32064f8ed89 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include // Options to control how MicroPython is built for this port, // overriding defaults in py/mpconfig.h. diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index de0f5923bafe9..15698a826547b 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "py/runtime.h" #include "py/mpthread.h" diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index e87b5d8ec0ff3..7e10557ba5e87 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include // options to control how MicroPython is built diff --git a/ports/windows/windows_mphal.c b/ports/windows/windows_mphal.c index b442b59147e4e..4491d18862782 100644 --- a/ports/windows/windows_mphal.c +++ b/ports/windows/windows_mphal.c @@ -32,6 +32,7 @@ #include #include #include +#include HANDLE std_in = NULL; HANDLE con_out = NULL; diff --git a/py/argcheck.c b/py/argcheck.c index c333ead05b64a..ea3a5effd954e 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include "py/runtime.h" diff --git a/py/bc.c b/py/bc.c index 34bc78fd3a588..ff7ef007c04ee 100644 --- a/py/bc.c +++ b/py/bc.c @@ -27,7 +27,6 @@ #include #include -#include #include "py/runtime.h" #include "py/bc0.h" diff --git a/py/binary.c b/py/binary.c index d0f72ec23c49c..524d9eda83fdd 100644 --- a/py/binary.c +++ b/py/binary.c @@ -26,10 +26,8 @@ */ #include -#include #include #include -#include #include "py/binary.h" #include "py/smallint.h" diff --git a/py/builtinimport.c b/py/builtinimport.c index bdc82e77c8f74..df5ac5f8a0994 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -27,6 +27,7 @@ #include #include +#include #include #include "py/compile.h" diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 2853da26c5465..cffaa4bb89950 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -108,7 +108,7 @@ STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint return 0; } const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); - if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) { + if (!(strlen(p) == 2 && p[0] == 'r' && (mp_uint_t)p[1] == '0' + i)) { emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3")); return 0; } diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index 6cc2e4d34b6a8..5dac2ae3907f1 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -92,7 +92,7 @@ STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uin return 0; } const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); - if (!(strlen(p) == 2 && p[0] == 'a' && p[1] == '2' + i)) { + if (!(strlen(p) == 2 && p[0] == 'a' && (mp_uint_t)p[1] == '2' + i)) { emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5")); return 0; } diff --git a/py/gc.c b/py/gc.c index 9c6336852a0cd..767f1b81c4dfa 100644 --- a/py/gc.c +++ b/py/gc.c @@ -298,7 +298,8 @@ STATIC void gc_sweep(void) { #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected)++; #endif - // fall through to free the head + // fall through to free the head + MP_FALLTHROUGH case AT_TAIL: if (free_tail) { diff --git a/py/lexer.c b/py/lexer.c index 7d2a251d41d75..07ea2b96ab578 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -346,7 +346,8 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) { vstr_add_char(&lex->vstr, '\\'); break; } - // Otherwise fall through. + // Otherwise fall through. + MP_FALLTHROUGH case 'x': { mp_uint_t num = 0; if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) { diff --git a/py/malloc.c b/py/malloc.c index c775d5b157312..205eb4ce3ee33 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -25,7 +25,7 @@ */ #include -#include +#include #include #include "py/mpconfig.h" @@ -62,6 +62,7 @@ #define realloc(ptr, n) gc_realloc(ptr, n, true) #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) #else +#include // GC is disabled. Use system malloc/realloc/free. diff --git a/py/map.c b/py/map.c index 54f4b0204b87f..9b559a604b1b8 100644 --- a/py/map.c +++ b/py/map.c @@ -25,7 +25,7 @@ */ #include -#include +#include #include #include diff --git a/py/modmath.c b/py/modmath.c index b7948f39e7c91..3ab3ff334c41b 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -206,8 +206,8 @@ MATH_FUN_1(lgamma, lgamma) STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol }; static const mp_arg_t allowed_args[] = { - {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, - {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}}, }; diff --git a/py/modstruct.c b/py/modstruct.c index 4cbcad6d496a8..0c0e3e722aa69 100644 --- a/py/modstruct.c +++ b/py/modstruct.c @@ -25,7 +25,6 @@ * THE SOFTWARE. */ -#include #include #include "py/runtime.h" diff --git a/py/moduerrno.c b/py/moduerrno.c index d9affd9b20c7a..2f5821f2b0ed2 100644 --- a/py/moduerrno.c +++ b/py/moduerrno.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include #include #include "py/obj.h" diff --git a/py/mpconfig.h b/py/mpconfig.h index cc83f3850d695..854188b66b585 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1643,6 +1643,13 @@ typedef double mp_float_t; #endif #endif +// Explicitly annotate switch case fall throughs +#if defined(__GNUC__) && __GNUC__ >= 7 +#define MP_FALLTHROUGH __attribute__((fallthrough)); +#else +#define MP_FALLTHROUGH +#endif + #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE #define MP_HTOBE16(x) ((uint16_t)((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))) diff --git a/py/mphal.h b/py/mphal.h index 0d4b1224e5cec..787b489836f1f 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_MPHAL_H #include +#include #include "py/mpconfig.h" #ifdef MICROPY_MPHALPORT_H diff --git a/py/nlr.h b/py/nlr.h index f9fbf56e54e60..00c994a56303b 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -29,9 +29,6 @@ // non-local return // exception handling, basically a stack of setjmp/longjmp buffers -#include -#include - #include "py/mpconfig.h" #define MICROPY_NLR_NUM_REGS_X86 (6) @@ -79,6 +76,9 @@ #define MICROPY_NLR_POWERPC (1) // this could be less but using 128 for safety #define MICROPY_NLR_NUM_REGS (128) +#elif defined(__riscv_xlen) && __riscv_xlen == 32 + #define MICROPY_NLR_RISCV32 (1) + #define MICROPY_NLR_NUM_REGS (14) #else #define MICROPY_NLR_SETJMP (1) //#warning "No native NLR support for this arch, using setjmp implementation" @@ -150,6 +150,7 @@ NORETURN void nlr_jump_fail(void *val); #ifndef MICROPY_DEBUG_NLR #define nlr_raise(val) nlr_jump(MP_OBJ_TO_PTR(val)) #else +#include #include "mpstate.h" #define nlr_raise(val) \ do { \ diff --git a/py/nlrriscv32.c b/py/nlrriscv32.c new file mode 100644 index 0000000000000..af8d773d1a694 --- /dev/null +++ b/py/nlrriscv32.c @@ -0,0 +1,89 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Emil Renner Berthing + * + * 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 "py/mpstate.h" + +#if MICROPY_NLR_RISCV32 + +#undef nlr_push + +// For reference, riscv callee save regs are: +// s0-s11, ra and sp + +__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { + + __asm volatile ( + "sw s0, 0+%0 \n" // store s0 into nlr->regs + "sw s1, 4+%0 \n" // store s1 into nlr->regs + "sw s2, 8+%0 \n" // store s2 into nlr->regs + "sw s3, 12+%0 \n" // store s3 into nlr->regs + "sw s4, 16+%0 \n" // store s4 into nlr->regs + "sw s5, 20+%0 \n" // store s5 into nlr->regs + "sw s6, 24+%0 \n" // store s6 into nlr->regs + "sw s7, 28+%0 \n" // store s7 into nlr->regs + "sw s8, 32+%0 \n" // store s8 into nlr->regs + "sw s9, 36+%0 \n" // store s9 into nlr->regs + "sw s10, 40+%0 \n" // store s10 into nlr->regs + "sw s11, 44+%0 \n" // store s11 into nlr->regs + "sw ra, 48+%0 \n" // store ra into nlr->regs + "sw sp, 52+%0 \n" // store sp into nlr->regs + "tail nlr_push_tail \n" // do the rest in C + : "=o" (nlr->regs) + ); +} + +NORETURN void nlr_jump(void *val) { + MP_NLR_JUMP_HEAD(val, top) + + /* WARNING: if the above macro grows to call a function + * (that returns) after loading top, the compiler might + * decide to stash top in an s-register and the assembly + * below will break. + */ + __asm volatile ( + "lw s0, 0+%0 \n" // load s0 from top->regs + "lw s1, 4+%0 \n" // load s1 from top->regs + "lw s2, 8+%0 \n" // load s2 from top->regs + "lw s3, 12+%0 \n" // load s3 from top->regs + "lw s4, 16+%0 \n" // load s4 from top->regs + "lw s5, 20+%0 \n" // load s5 from top->regs + "lw s6, 24+%0 \n" // load s6 from top->regs + "lw s7, 28+%0 \n" // load s7 from top->regs + "lw s8, 32+%0 \n" // load s8 from top->regs + "lw s9, 36+%0 \n" // load s9 from top->regs + "lw s10, 40+%0 \n" // load s10 from top->regs + "lw s11, 44+%0 \n" // load s11 from top->regs + "lw ra, 48+%0 \n" // load ra from top->regs + "lw sp, 52+%0 \n" // load sp from top->regs + "li a0, 1 \n" // return 1, non-local return + "ret \n" // return + :: "o" (top->regs) + ); + + MP_UNREACHABLE +} + +#endif // MICROPY_NLR_RISCV32 diff --git a/py/objbool.c b/py/objbool.c index 23e023d8cbc15..cce62bf663c57 100644 --- a/py/objbool.c +++ b/py/objbool.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include "py/runtime.h" diff --git a/py/objcomplex.c b/py/objcomplex.c index f4c4aeffcb91d..4d952ab4361d4 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include diff --git a/py/objenumerate.c b/py/objenumerate.c index d1de4add47ba8..0e34da46af4f0 100644 --- a/py/objenumerate.c +++ b/py/objenumerate.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include "py/runtime.h" diff --git a/py/objexcept.c b/py/objexcept.c index 517427ed71365..885032c3e3eed 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -208,7 +208,7 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, siz // reserved room (after the traceback data) for a tuple with 1 element. // Otherwise we are free to use the whole buffer after the traceback data. if (o_tuple == NULL && mp_emergency_exception_buf_size >= - EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args)) { + (mp_int_t)(EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args))) { o_tuple = (mp_obj_tuple_t *) ((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET); } @@ -383,7 +383,7 @@ mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, mp_rom_error_te // that buffer to store the string object, reserving room at the start for the // traceback and 1-tuple. if (o_str == NULL - && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t)) { + && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t))) { o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET); } @@ -465,7 +465,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_er // that buffer to store the string object and its data (at least 16 bytes for // the string data), reserving room at the start for the traceback and 1-tuple. if ((o_str == NULL || o_str_buf == NULL) - && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16) { + && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16)) { used_emg_buf = true; o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET); o_str_buf = (byte *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_BUF_OFFSET); @@ -573,7 +573,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); if (self->traceback_data == NULL) { #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF - if (mp_emergency_exception_buf_size >= EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE) { + if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) { // There is room in the emergency buffer for traceback data size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TRACEBACK_OFFSET); diff --git a/py/objfloat.c b/py/objfloat.c index 451609492e5c1..80c2ac3f464ac 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include #include diff --git a/py/objgenerator.c b/py/objgenerator.c index 543685fac7860..a059af8e5c5f6 100644 --- a/py/objgenerator.c +++ b/py/objgenerator.c @@ -25,8 +25,7 @@ * THE SOFTWARE. */ -#include -#include +#include #include "py/runtime.h" #include "py/bc.h" diff --git a/py/objgetitemiter.c b/py/objgetitemiter.c index 54e27b8f11d0b..409fcefda5701 100644 --- a/py/objgetitemiter.c +++ b/py/objgetitemiter.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include "py/runtime.h" diff --git a/py/objint.c b/py/objint.c index 4619fb5751fed..f2afb05365e17 100644 --- a/py/objint.c +++ b/py/objint.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include #include diff --git a/py/objint_longlong.c b/py/objint_longlong.c index f2e88c3ea5643..9d04ea45c18b4 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -25,8 +25,9 @@ * THE SOFTWARE. */ -#include +#include #include +#include #include "py/smallint.h" #include "py/objint.h" diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 6e52073a6e85b..7efe84e649946 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -26,6 +26,7 @@ #include #include +#include #include #include "py/parsenumbase.h" diff --git a/py/objmap.c b/py/objmap.c index 78c52c8925782..f4db0e7092c9b 100644 --- a/py/objmap.c +++ b/py/objmap.c @@ -24,8 +24,7 @@ * THE SOFTWARE. */ -#include -#include +#include #include "py/runtime.h" diff --git a/py/objmodule.c b/py/objmodule.c index a1f9d9d7f146a..2fce32e40ca79 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -25,9 +25,8 @@ * THE SOFTWARE. */ -#include +#include #include -#include #include "py/objmodule.h" #include "py/runtime.h" diff --git a/py/objnone.c b/py/objnone.c index 271a8543f9718..b27ba86a700e7 100644 --- a/py/objnone.c +++ b/py/objnone.c @@ -24,8 +24,6 @@ * THE SOFTWARE. */ -#include - #include "py/obj.h" #if !MICROPY_OBJ_IMMEDIATE_OBJS diff --git a/py/objobject.c b/py/objobject.c index 00082dfe08708..4c2787ba82d2f 100644 --- a/py/objobject.c +++ b/py/objobject.c @@ -24,7 +24,7 @@ * THE SOFTWARE. */ -#include +#include #include "py/objtype.h" #include "py/runtime.h" diff --git a/py/objpolyiter.c b/py/objpolyiter.c index 01880bff9c1b9..27936a1256262 100644 --- a/py/objpolyiter.c +++ b/py/objpolyiter.c @@ -24,8 +24,6 @@ * THE SOFTWARE. */ -#include - #include "py/runtime.h" // This is universal iterator type which calls "iternext" method stored in diff --git a/py/objproperty.c b/py/objproperty.c index 8d2c292c52d76..cdd8779dd192b 100644 --- a/py/objproperty.c +++ b/py/objproperty.c @@ -24,8 +24,7 @@ * THE SOFTWARE. */ -#include -#include +#include #include "py/runtime.h" diff --git a/py/objrange.c b/py/objrange.c index 4eed4b9410550..1f9acbc56346e 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -24,7 +24,8 @@ * THE SOFTWARE. */ -#include +#include +#include #include "py/runtime.h" diff --git a/py/objreversed.c b/py/objreversed.c index 4254668e751c8..8a4a97f310c2d 100644 --- a/py/objreversed.c +++ b/py/objreversed.c @@ -24,8 +24,7 @@ * THE SOFTWARE. */ -#include -#include +#include #include "py/runtime.h" diff --git a/py/objset.c b/py/objset.c index f31a901a7008a..dac9b1138291e 100644 --- a/py/objset.c +++ b/py/objset.c @@ -445,6 +445,7 @@ STATIC mp_obj_t set_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } return MP_OBJ_NEW_SMALL_INT(hash); } + MP_FALLTHROUGH #endif default: return MP_OBJ_NULL; // op not supported diff --git a/py/objsingleton.c b/py/objsingleton.c index 2b896305cff5b..a7ce92b06f211 100644 --- a/py/objsingleton.c +++ b/py/objsingleton.c @@ -24,9 +24,6 @@ * THE SOFTWARE. */ -#include -#include - #include "py/obj.h" /******************************************************************************/ diff --git a/py/objslice.c b/py/objslice.c index c65c30601be69..e493b7e3cd520 100644 --- a/py/objslice.c +++ b/py/objslice.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include #include #include "py/runtime.h" diff --git a/py/objzip.c b/py/objzip.c index 4abc917c3f525..ba0f38940d5cc 100644 --- a/py/objzip.c +++ b/py/objzip.c @@ -24,8 +24,7 @@ * THE SOFTWARE. */ -#include -#include +#include #include "py/objtuple.h" #include "py/runtime.h" diff --git a/py/parsenum.c b/py/parsenum.c index e665da7d8cdad..5ecbea11e2a17 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -25,7 +25,8 @@ */ #include -#include +#include +#include #include "py/runtime.h" #include "py/parsenumbase.h" diff --git a/py/profile.c b/py/profile.c index 863b0068a03e5..abf71618fc750 100644 --- a/py/profile.c +++ b/py/profile.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include + #include "py/profile.h" #include "py/bc0.h" #include "py/gc.h" diff --git a/py/py.mk b/py/py.mk index d864a7ed3d843..5973336085bc6 100644 --- a/py/py.mk +++ b/py/py.mk @@ -55,6 +55,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ nlrthumb.o \ nlrpowerpc.o \ nlrxtensa.o \ + nlrriscv32.o \ nlrsetjmp.o \ malloc.o \ gc.o \ diff --git a/py/pystack.h b/py/pystack.h index ea8fddcf2f65b..99443c1e83340 100644 --- a/py/pystack.h +++ b/py/pystack.h @@ -33,6 +33,7 @@ #define MP_PYSTACK_DEBUG (0) #if MICROPY_ENABLE_PYSTACK +#include void mp_pystack_init(void *start, void *end); void *mp_pystack_alloc(size_t n_bytes); diff --git a/py/qstr.c b/py/qstr.c index c14ec5ae0073d..10266e1e6ae06 100644 --- a/py/qstr.c +++ b/py/qstr.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include #include #include diff --git a/py/reader.c b/py/reader.c index d68406b1c68b3..b3f88dcc77b48 100644 --- a/py/reader.c +++ b/py/reader.c @@ -25,7 +25,6 @@ */ #include -#include #include "py/runtime.h" #include "py/mperrno.h" diff --git a/py/runtime.h b/py/runtime.h index 0bf988b905656..3b956939ea6c3 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -174,6 +174,7 @@ NORETURN void mp_raise_recursion_depth(void); #else // A port may define to raise TypeError for example #ifndef mp_check_self +#include #define mp_check_self(pred) assert(pred) #endif #endif diff --git a/py/scope.c b/py/scope.c index 073c2a38c2c21..98e02fb53fe31 100644 --- a/py/scope.c +++ b/py/scope.c @@ -72,7 +72,7 @@ void scope_free(scope_t *scope) { m_del(scope_t, scope, 1); } -id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, scope_kind_t kind) { +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, id_info_kind_t kind) { id_info_t *id_info = scope_find(scope, qst); if (id_info != NULL) { return id_info; diff --git a/py/scope.h b/py/scope.h index b52d98ea1c72d..edf164c4adace 100644 --- a/py/scope.h +++ b/py/scope.h @@ -29,14 +29,14 @@ #include "py/parse.h" #include "py/emitglue.h" -enum { +typedef enum { ID_INFO_KIND_UNDECIDED, ID_INFO_KIND_GLOBAL_IMPLICIT, ID_INFO_KIND_GLOBAL_EXPLICIT, ID_INFO_KIND_LOCAL, // in a function f, written and only referenced by f ID_INFO_KIND_CELL, // in a function f, read/written by children of f ID_INFO_KIND_FREE, // in a function f, belongs to the parent of f -}; +} id_info_kind_t; enum { ID_FLAG_IS_PARAM = 0x01, @@ -92,7 +92,7 @@ typedef struct _scope_t { scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_uint_t emit_options); void scope_free(scope_t *scope); -id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, scope_kind_t kind); +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, id_info_kind_t kind); id_info_t *scope_find(scope_t *scope, qstr qstr); id_info_t *scope_find_global(scope_t *scope, qstr qstr); void scope_check_to_close_over(scope_t *scope, id_info_t *id); diff --git a/py/stream.c b/py/stream.c index dce64a44dbeda..65f8c0792a082 100644 --- a/py/stream.c +++ b/py/stream.c @@ -431,13 +431,13 @@ STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { struct mp_stream_seek_t seek_s; // TODO: Could be uint64 seek_s.offset = mp_obj_get_int(args[1]); - seek_s.whence = SEEK_SET; + seek_s.whence = MP_SEEK_SET; if (n_args == 3) { seek_s.whence = mp_obj_get_int(args[2]); } // In POSIX, it's error to seek before end of stream, we enforce it here. - if (seek_s.whence == SEEK_SET && seek_s.offset < 0) { + if (seek_s.whence == MP_SEEK_SET && seek_s.offset < 0) { mp_raise_OSError(MP_EINVAL); } @@ -455,7 +455,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj, 2, 3, stream_seek); STATIC mp_obj_t stream_tell(mp_obj_t self) { mp_obj_t offset = MP_OBJ_NEW_SMALL_INT(0); - mp_obj_t whence = MP_OBJ_NEW_SMALL_INT(SEEK_CUR); + mp_obj_t whence = MP_OBJ_NEW_SMALL_INT(MP_SEEK_CUR); const mp_obj_t args[3] = {self, offset, whence}; return stream_seek(3, args); } diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 068214bf916cf..7912270872759 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -30,6 +30,10 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winitializer-overrides" #endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverride-init" +#endif // __GNUC__ >= 5 static const void *const entry_table[256] = { [0 ... 255] = &&entry_default, @@ -119,3 +123,6 @@ static const void *const entry_table[256] = { #if __clang__ #pragma clang diagnostic pop #endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic pop +#endif // __GNUC__ >= 5 diff --git a/tests/run-tests b/tests/run-tests index eebc8c4252700..8d4efa4f61245 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -634,7 +634,7 @@ the last matching regex is used: sys.exit(0) LOCAL_TARGETS = ('unix', 'qemu-arm',) - EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'esp32', 'minimal', 'nrf') + EXTERNAL_TARGETS = ('pyboard', 'wipy', 'esp8266', 'esp32', 'minimal', 'nrf', 'gd32vf103') if args.target in LOCAL_TARGETS or args.list_tests: pyb = None elif args.target in EXTERNAL_TARGETS: