diff --git a/ports/quectel/Makefile b/ports/quectel/Makefile new file mode 100644 index 0000000000000..95f02bec6579e --- /dev/null +++ b/ports/quectel/Makefile @@ -0,0 +1,109 @@ +TOP := ../.. +ifneq ($(HeliosSDK_PATH),) +ROOT := $(HeliosSDK_PATH) +else +ROOT := $(TOP)/../.. +endif + +include ../../py/mkenv.mk +include quectel.mk + +CROSS = 1 + +ifeq ($(CROSS), 1) +CFLAGS = $(INC) $(QUEC_MOD_CFLAGS) $(PLAT_CFLAGS) $(PLAT_DFLAGS) $(COPT) +else +LD = gcc +CFLAGS = $(INC) -Wall -Werror -Wdouble-promotion -Wfloat-conversion -std=c99 $(COPT) +LDFLAGS = -Wl,-Map=$@.map,--cref -Wl,--gc-sections +endif + +CSUPEROPT = -Os # save some code space + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +# MicroPython feature configurations +MICROPY_ROM_TEXT_COMPRESSION ?= 1 + +FROZEN_MANIFEST = $(QUEC_PY_MOD) + +ifeq ($(OS),Windows_NT) +export MICROPY_MPYCROSS = $(TOP)/mpy-cross/build/mpy-cross.exe +else +export MICROPY_MPYCROSS = $(TOP)/mpy-cross/build/mpy-cross +endif + +# include py core make definitions +include $(TOP)/py/py.mk +include $(TOP)/extmod/extmod.mk + +ifeq ($(CROSS), 1) +CROSS_COMPILE ?= arm-none-eabi- +endif + +INC += -I. +INC += -I$(TOP) +INC += -I$(BUILD) +INC += $(MICROPYTHON_CFLAGS_INC) +INC += $(QUEC_INC) + +# Tune for Debugging or Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -O0 -ggdb +else +CFLAGS += -Os -DNDEBUG +CFLAGS += -fdata-sections -ffunction-sections +endif + +# Flags for optional C++ source code +CXXFLAGS += $(filter-out -std=c99,$(CFLAGS)) +CXXFLAGS += $(CXXFLAGS_MOD) + +# Flags for user C modules +CFLAGS += $(CFLAGS_MOD) +LDFLAGS += $(LDFLAGS_MOD) + +LIBS = lib/littlefs/lfs2.c \ + lib/littlefs/lfs2_util.c + +SHARED_SRC_C = shared/libc/printf.c \ + shared/readline/readline.c \ + shared/runtime/pyexec.c \ + shared/runtime/sys_stdio_mphal.c \ + shared/runtime/stdout_helpers.c \ + shared/netutils/netutils.c \ + shared/runtime/interrupt_char.c \ + shared/timeutils/timeutils.c + +SRC_MOD += $(QUEC_SRC_MOD) + +SRC_C += $(SRC_MOD) \ + $(SHARED_SRC_C) \ + $(SRC_EXTMOD_C) \ + $(LIBS) \ + $(QUEC_SRC) \ + $(BUILD)/frozen_content.c + +SRC_CXX += $(SRC_MOD_CXX) + +SRC_QSTR += $(SRC_MOD) $(SRC_MOD_CXX) $(SHARED_SRC_C) $(SRC_EXTMOD_C) + +OBJ += $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) + +ifeq ($(CROSS), 1) +all: $(BUILD)/firmware.a +else +all: $(BUILD)/firmware.elf +endif + +$(BUILD)/_frozen_mpy.c: frozentest.mpy $(BUILD)/genhdr/qstrdefs.generated.h + $(ECHO) "MISC freezing bytecode" + $(Q)$(PYTHON) $(TOP)/tools/mpy-tool.py -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h -mlongint-impl=none $< > $@ + +$(BUILD)/firmware.a: $(OBJ) + $(ECHO) "AR $@" + $(Q)$(AR) -cr $@ $^ + +include $(TOP)/py/mkrules.mk diff --git a/ports/quectel/README.md b/ports/quectel/README.md new file mode 100644 index 0000000000000..a0fcb3dd74944 --- /dev/null +++ b/ports/quectel/README.md @@ -0,0 +1,136 @@ +MicroPython port to the Quectel +============================= + +This is a port of MicroPython to the Quectel series of +microcontrollers. It uses the HeliosSDK and MicroPython runs as +a task under FreeRTOS. + +Support Eigencomm, Unisoc, Qualcomm and ASR cellular modules. + +Supported features include: +- REPL (Python prompt) over usb. +- Python 3.4 syntax and built-in rich functional modules. +- The machine module with GPIO, EXTINT, UART, SPI, ADC, WDT RTC, and Timer. +- The network module with cellular modem support. +- etc. + +Setting up HeliosSDK and the build environment +-------------------------------------------- + +MicroPython on quectel port requires the HeliosSDK. The HeliosSDK includes the libraries and RTOS needed to +manage the quectel microcontroller, as well as a way to manage the required +build environment and toolchains needed to build the firmware. + +To install the HeliosSDK the full instructions can be found at the +[HeliosSDK Development Guide](https://python.quectel.com/doc/Application_guide/zh/helios-sdk/quick-start.html). + +**Windows 10 Environment Setup** + +*Step 1: Download Toolchain* + +Download the toolchain installation package [helios-toolchain.exe](https://github.com/QuecPython/toolchain/releases/tag/V1.4.2) for Windows 10 environment from the QuecPython official website. + +*Step 2: Install Toolchain* + +Run helios-toolchain.exe as an administrator, as shown in the following figure, and click "**Install**" to install the toolchain. + +> The target folder **must not** contain spaces. + +**Ubuntu Environment Setup** + +*Step 1: Download Toolchain* + +Download the toolchain installation package [helios-toolchain](https://github.com/QuecPython/toolchain/releases/tag/V1.1.0) for Ubuntu 16.04 environment from the QuecPython official website. + +*Step 2: Install Toolchain* + +Place the installation package in the same location as the target folder. Execute the following command to install the toolchain. +`sudo ./helios-toolchain` + +*Step 3: Install Other Tools* + +Enter the following command in the terminal to install `p7zip-full`, `git`, `make`, and `python3`. +``` +sudo apt install p7zip-full git make python3 +``` + +*Source Code* + +For HeliosSDK, please contact the [QuecPython technical team](https://python.quectel.com/en/contact) for the source code of HeliosSDK(We are preparing to open source), You can also get supports through email QuecPython@quectel.com. + +For MicroPython, You can directly pull the official code, but you need to pull MicroPython into the heliossdk directory, for example, create a services directory under heliossdk and place MicroPython in the services directory. + +Building the firmware +--------------------- + +Before you start building the firmware, you must build the MicroPython cross compiler firstly, it will be used to pre-compile some of the built-in scripts to bytecode. + +> If you are in a Windows environment, you need a Windows compilation toolchain, such as MinGW. + +```bash +# path: heliossdk/services/micropython +$ make -C mpy-cross +``` + +Then to build MicroPython for the quectel run: + +```bash +# path: heliossdk/services/micropython +$ cd ports/quectel +$ make submodules +$ make +``` + +This will produce a combined `firmware.a` lib in the `build/` +subdirectory directly.The compiled MicroPython library will be used by HeliosSDK to build a complete firmware package. Of course, you can also build the entire firmware directly using the following method. + +*Check the usage of the helios compilation commands* + +In the command line started in the HeliosSDK directory, type `helios` and press "**Enter**" to view the usage of the helios command. +The output is as follows: + +``` +Usage: helios [] [] [] + +These are common commands used in various situations: + menuconfig - Do the project configuration + make [[] []] - Do the compilation work + private_clean - Clean the app private target + clean - Clean the output directory + git [] - Git commands execution + help - Show this help page +``` + +> For detailed usage of the compilation command, please refer to `README.MD` in the SDK root directory. + +*Compile the firmware* + +Taking the EG915UEC_AC module as an example, type the following command in the command line and press "**Enter**": +``` +helios make services/micropython @EG915UEC_AC EG915UECACR03A01M08 +``` + +- `helios`: Trigger the compilation process. +- `make`: Compile the firmware. +- `services/micropython`: Application entry address (relative to the SDK root directory, according to the requirements of the host system, the Win32 platform is \ and the Linux platform is /). It can be adjusted according to the specific location of MicroPython. +- `@EG915UEC_AC`: Specify the target module model. You need to modify it according to your actual model. +- `EG915UECACR03A01M08`: Firmware version name, which can be omitted. You need to modify it according to your actual model. + +*Check the compilation target* + +The generated firmware package is stored in the *`output/release`* folder in the HeliosSDK root directory. + +To clean the compilation target, type the following command in the command line and press "**Enter**": +``` +helios clean +``` + +*Flash the firmware* + +You need to use QPYcom or VSCode tool to burn the firmware. Please refer to [Quectel_QFlash_User_Guide](https://python.quectel.com/doc/Getting_started/en/flash_firmware.html) for firmware flashing. + + +Accessing the board +------------------------------------- + +You can access via the `USB REPL` port, which stands for `Read-Eval-Print-Loop` (interactive interpreter). Please refer to [Quectel_Getting_Started](https://python.quectel.com/doc/Getting_started/en/REPL_dev.html) for firmware debugging. diff --git a/ports/quectel/boards/EC600UCN_LB/board.json b/ports/quectel/boards/EC600UCN_LB/board.json new file mode 100644 index 0000000000000..0aabf37d25262 --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "ec600ucn_lb.jpg" + ], + "mcu": "ec600ucnlb", + "product": "EC600UCN_LB", + "thumbnail": "", + "url": "https://developer.quectel.com/modules-cat/ec600u-series", + "vendor": "Unisoc" +} diff --git a/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h b/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h new file mode 100644 index 0000000000000..6b68f6af78f16 --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/mpconfigboard.h @@ -0,0 +1 @@ +// Board and hardware specific configuration diff --git a/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk b/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk new file mode 100644 index 0000000000000..139597f9cb07c --- /dev/null +++ b/ports/quectel/boards/EC600UCN_LB/mpconfigvariant.mk @@ -0,0 +1,2 @@ + + diff --git a/ports/quectel/boards/EG915UEC_AC/board.json b/ports/quectel/boards/EG915UEC_AC/board.json new file mode 100644 index 0000000000000..aac840787d6b5 --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/board.json @@ -0,0 +1,15 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [], + "images": [ + "eg915uec_ac.jpg" + ], + "mcu": "eg915uecac", + "product": "EG915UEC_AC", + "thumbnail": "", + "url": "https://developer.quectel.com/en/modules-cat/eg915u-series", + "vendor": "Unisoc" +} \ No newline at end of file diff --git a/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h b/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h new file mode 100644 index 0000000000000..6b68f6af78f16 --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/mpconfigboard.h @@ -0,0 +1 @@ +// Board and hardware specific configuration diff --git a/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk b/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk new file mode 100644 index 0000000000000..139597f9cb07c --- /dev/null +++ b/ports/quectel/boards/EG915UEC_AC/mpconfigvariant.mk @@ -0,0 +1,2 @@ + + diff --git a/ports/quectel/boards/deploy.md b/ports/quectel/boards/deploy.md new file mode 100644 index 0000000000000..d23eaced8cbbb --- /dev/null +++ b/ports/quectel/boards/deploy.md @@ -0,0 +1,3 @@ +*Flash the firmware* + +You need to use QPYcom or VSCode tool to burn the firmware. Please refer to [Quectel_QFlash_User_Guide](https://python.quectel.com/doc/Getting_started/en/flash_firmware.html) for firmware flashing. diff --git a/ports/quectel/gccollect.c b/ports/quectel/gccollect.c new file mode 100644 index 0000000000000..6fee0046a0ff1 --- /dev/null +++ b/ports/quectel/gccollect.c @@ -0,0 +1,79 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 +#include + +#include "mpstate.h" +#include "gc.h" +#include "mpthread.h" +#include "gccollect.h" + +unsigned int ReadSP(void) { + uint32_t res; + __asm volatile ( + "mov %0, r13\n" + : "=r" (res) + : + : + ); + + return res; +} + +void gc_stacktop_set(void *ptr) { + MP_STATE_PORT(global_stacktop_ptr) = ptr; +} + +void gc_collect(void) { + // get current time, in case we want to time the GC + #if 0 + uint32_t start = mp_hal_ticks_us(); + #endif + // start the GC + gc_collect_start(); + // get the registers and the sp + uintptr_t sp = (uintptr_t)ReadSP(); + + if (mp_is_python_thread()) { + // trace the stack, including the registers (since they live on the stack in this function) + gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + } else if (NULL != MP_STATE_PORT(global_stacktop_ptr)) { + gc_collect_root((void **)sp, ((uint32_t)MP_STATE_PORT(global_stacktop_ptr) - sp) / sizeof(uint32_t)); + } else { + // do nothing + } + + // trace root pointers from any threads + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif + + // end the GC + gc_collect_end(); +} + +MP_REGISTER_ROOT_POINTER(volatile void *global_stacktop_ptr); diff --git a/ports/quectel/gccollect.h b/ports/quectel/gccollect.h new file mode 100644 index 0000000000000..6b3521ef46c1e --- /dev/null +++ b/ports/quectel/gccollect.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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. + */ + +#ifndef __MICROPY_INCLUDED_QUECTEL_GCCOLLECT_H__ +#define __MICROPY_INCLUDED_QUECTEL_GCCOLLECT_H__ + +void gc_stacktop_set(void *ptr); +void gc_collect(void); + +// Set a stack address as the end address of the GC collection +// Do not add {} to the macro definition to prevent the scoping of stack_dummy from changing +#define GC_STACKTOP_SET() int stack_dummy; gc_stacktop_set(&stack_dummy); + +// Clears the gc collection end stack pointer for global non-Python threads +#define GC_STACKTOP_CLEAR() gc_stacktop_set(NULL); + +#endif diff --git a/ports/quectel/help.c b/ports/quectel/help.c new file mode 100644 index 0000000000000..fadb2f60e5195 --- /dev/null +++ b/ports/quectel/help.c @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 "py/builtin.h" + +const char quecpython_help_text[] = + "Welcome to QuecPython! The Quecpython is a \n" + "small microcontroller that runs MicroPython.\n" + "\n" + "For online help please visit https://python.quectel.com/wiki \n" + "\n" + "Control commands:\n" + " CTRL-A -- on a blank line, enter raw REPL mode\n" + " CTRL-B -- on a blank line, enter normal REPL mode\n" + " CTRL-C -- interrupt a running program\n" + " CTRL-D -- on a blank line, do a soft reset of the board\n" + " CTRL-E -- on a blank line, enter paste mode\n" + "\n" + "For further help on a specific object, type help(obj)\n" + "For a list of available modules, type help('modules')\n" +; diff --git a/ports/quectel/main.c b/ports/quectel/main.c new file mode 100644 index 0000000000000..70b46f30df852 --- /dev/null +++ b/ports/quectel/main.c @@ -0,0 +1,285 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 +#include +#include +#include + +#include "py/builtin.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/objmodule.h" +#include "py/stackctrl.h" +#include "py/mphal.h" +#include "shared/runtime/pyexec.h" +#include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" +#include +#include "helios.h" +#include "helios_os.h" +#include "helios_rtc.h" +#include "helios_debug.h" +#include "mphalport.h" +#if MICROPY_KBD_EXCEPTION +#include "shared/runtime/interrupt_char.h" +#endif + +#if CONFIG_MBEDTLS +#include "mbedtls_init.h" +#endif + + +Helios_Thread_t ql_micropython_task_ref; + + +#define MP_TASK_STACK_SIZE (MP_QPY_TASK_STACK_SIZE) +#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE / sizeof(uint32_t)) + +#if (defined(PLAT_Unisoc)) +#define QPY_ASSERT_SUPPORT 1 +#endif + +extern uint64_t mp_time_start_second; + +void nlr_jump_fail(void *val) { + #if QPY_ASSERT_SUPPORT + Helios_Assert(__func__, __FILE__, __LINE__, ""); + #endif + while (1) { + ; + } +} + +void NORETURN __fatal_error(const char *msg) { + #if QPY_ASSERT_SUPPORT + Helios_Assert(msg, __FILE__, __LINE__, ""); + #endif + while (1) { + ; + } +} + +#ifndef NDEBUG +void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { + #if QPY_ASSERT_SUPPORT + Helios_Assert(expr, file, line, ""); + #else + printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); + #endif + __fatal_error("Assertion failed"); +} + +#else +void __assert_fail(const char *__message, + const char *__file, int __line, + const char *__function) { + #if QPY_ASSERT_SUPPORT + Helios_Assert(__message, __file, __line, ""); + #else + printf("Assertion '%s' failed, at file %s:%d\n", __message, __file, __line); + #endif + __fatal_error("Assertion failed"); +} +#endif + +static char *stack_top; +#if MICROPY_ENABLE_GC +static char heap[MICROPY_GC_HEAP_SIZE]; +#endif + +extern pyexec_mode_kind_t pyexec_mode_kind; + +#if MICROPY_PY_KBD_EXCEPTION +MAINPY_RUNNING_FLAG_DEF +MAINPY_INTERRUPT_BY_KBD_FLAG_DEF + SET_MAINPY_RUNNING_TIMER_DEF +#endif + +#if MICROPY_PY_SOFT_RESET +static int vm_softreset_flag = 0; +#define SOFTRESET_FLAG_SET() vm_softreset_flag = 1; +#define SOFTRESET_FLAG_CLEAR() vm_softreset_flag = 0; +#define SOFTRESET_FLAG_TRUE() (1 == vm_softreset_flag) +#define SOFTRESET_FLAG_FALSE() (0 == vm_softreset_flag) +#endif +void quecpython_task(void *arg) { + int stack_dummy; + Helios_Thread_t id = 0; + void *stack_ptr = NULL; + #if MICROPY_QPY_MODULE_POC && CONFIG_POC_BND_XIN + helios_debug("start qpy_poc_register_task"); + extern void qpy_poc_register_task(); + qpy_poc_register_task(); + #endif + mp_mthread_sleep_deal_init(); + + #if CONFIG_MBEDTLS + mbedtls_platform_setup(NULL); + #endif + + #if MICROPY_PY_THREAD + id = Helios_Thread_GetID(); + ql_micropython_task_ref = id; + stack_ptr = Helios_Thread_GetStaskPtr(id); + #if defined(PLAT_ECR6600) || defined(PLAT_aic8800m40) + mp_thread_init(stack_ptr, MP_TASK_STACK_SIZE); // unit: Word + #else + mp_thread_init(stack_ptr, MP_TASK_STACK_LEN); + #endif + #endif + + if (mp_hal_stdio_init()) { + return; + } + +soft_reset: + mp_stack_set_top((void *)&stack_dummy); + mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024); + stack_top = (char *)&stack_dummy; + #if MICROPY_ENABLE_GC + gc_init(heap, heap + sizeof(heap)); + #endif + mp_init(); + #if MICROPY_ENABLE_CALLBACK_DEAL + qpy_callback_deal_init(); + #endif + readline_init0(); + + // run boot-up scripts + pyexec_frozen_module("_boot.py", false); + mp_time_start_second = Helios_RTC_GetSecond(); + + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL + #if MICROPY_PY_KBD_EXCEPTION + && MAINPY_INTERRUPT_BY_KBD_FLAG_FALSE() + #endif + ) { + #if MICROPY_PY_SOFT_RESET + SOFTRESET_FLAG_CLEAR(); + #endif + + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_RUNNING_FLAG_SET(); + #endif + + int ret = pyexec_file_if_exists("/main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_RUNNING_FLAG_CLEAR(); + if (RET_KBD_INTERRUPT == ret) { + MAINPY_INTERRUPT_BY_KBD_FLAG_SET(); + } + #endif + + #if MICROPY_PY_SOFT_RESET + if ((PYEXEC_SOFTRESET & ret) == PYEXEC_SOFTRESET) { + SOFTRESET_FLAG_SET(); + } + #endif + } else { + #if MICROPY_PY_KBD_EXCEPTION + MAINPY_INTERRUPT_BY_KBD_FLAG_CLEAR(); + #endif + #if MICROPY_PY_SOFT_RESET + SOFTRESET_FLAG_CLEAR(); + #endif + } + + if (1 + #if MICROPY_PY_KBD_EXCEPTION + && MAINPY_INTERRUPT_BY_KBD_FLAG_FALSE() + #endif + #if MICROPY_PY_SOFT_RESET + && SOFTRESET_FLAG_FALSE() + #endif + ) { + #if MICROPY_PY_SOFT_RESET + nlr_buf_t nlr; + nlr.ret_val = NULL; + if (nlr_push(&nlr) == 0) { + #endif + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + #if MICROPY_PY_SOFT_RESET + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + #endif + } + +soft_reset_exit: + + #if MICROPY_PY_THREAD + mp_thread_deinit(); + #endif + + gc_sweep_all(); + + mp_hal_stdout_tx_str("MPY: soft reboot\r\n"); + #if MICROPY_ENABLE_CALLBACK_DEAL + qpy_callback_deal_deinit(); + qpy_callback_para_link_free_all(); + #endif + mp_deinit(); + fflush(stdout); + goto soft_reset; +} + +#if !MICROPY_VFS +mp_lexer_t *mp_lexer_new_from_file(qstr filename) { + mp_raise_OSError(MP_ENOENT); +} + +mp_import_stat_t mp_import_stat(const char *path) { + (void)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); + +#endif + +application_init(quecpython_task, "quecpython_task", (MP_TASK_STACK_SIZE) / 1024, 0); diff --git a/ports/quectel/modflashdev.c b/ports/quectel/modflashdev.c new file mode 100644 index 0000000000000..4a9ea8f7f8d9e --- /dev/null +++ b/ports/quectel/modflashdev.c @@ -0,0 +1,191 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 "py/runtime.h" + +#if MICROPY_VFS +#include "extmod/vfs.h" +#endif + +#include "helios_flash.h" +#include "helios_debug.h" + +#define MOD_FLASHDEV_LOG(msg, ...) custom_log('flashdev', msg,##__VA_ARGS__) + +const mp_obj_type_t flash_device_type; + +struct lfs_flash_info +{ + uint32_t FlashType; + uint32_t LfsStartAddress; + uint32_t LfsEndAddress; +}; + +typedef struct flash_device_obj_t { + mp_obj_base_t base; + int block_size; + int block_count; + struct lfs_flash_info info; + char partition_name[32]; +} flash_device_obj_t; + +static mp_obj_t flash_init(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, 2, 2, false); + flash_device_obj_t *self = mp_obj_malloc(flash_device_obj_t, type); + + mp_buffer_info_t bufinfo; + memset(self->partition_name, 0x0, sizeof(self->partition_name)); + mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ); + if (bufinfo.len >= sizeof(self->partition_name)) { + mp_raise_ValueError(MP_ERROR_TEXT("partition name is invalid")); + } + memcpy(self->partition_name, bufinfo.buf, bufinfo.len); + + self->block_size = mp_obj_get_int(args[1]); + + if (self->block_size <= 0) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid block size")); + } + + HeliosFlashPartiCtx *FlashPartiCtx = Helios_Flash_GetPartiCtx((const char *)self->partition_name); + if (!FlashPartiCtx) { + mp_raise_OSError(19); + } + + uint32_t FlashPartiAddr = FlashPartiCtx->addr; + size_t FlashPartiSize = FlashPartiCtx->size; + MOD_FLASHDEV_LOG("flash addr:%x", FlashPartiAddr); + MOD_FLASHDEV_LOG("flash size:%x", FlashPartiSize); + self->info.LfsStartAddress = (FlashPartiAddr) / self->block_size * self->block_size; + #if MICROPY_VFS_LFS2 + self->info.LfsEndAddress = ((FlashPartiAddr + FlashPartiSize) / self->block_size) * self->block_size; + #else + self->info.LfsEndAddress = ((FlashPartiAddr + FlashPartiSize - 1) / self->block_size) * self->block_size; + #endif + + self->block_count = FlashPartiSize / self->block_size; + + return MP_OBJ_FROM_PTR(self); +} + +static mp_obj_t flash_readblocks(size_t n_args, const mp_obj_t *args) { + flash_device_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t FlashAddrss = 0; + uint32_t block_num = mp_obj_get_int(args[1]); + uint32_t offset = block_num * self->block_size; + mp_buffer_info_t bufinfo; + int ret; + + if (n_args >= 4) { + offset += mp_obj_get_int(args[3]); + } + + FlashAddrss = self->info.LfsStartAddress + offset; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_WRITE); + ret = Helios_Flash_Read((uint32_t)FlashAddrss, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flash_readblocks_obj, 3, 4, flash_readblocks); + +static mp_obj_t flash_writeblocks(size_t n_args, const mp_obj_t *args) { + flash_device_obj_t *self = MP_OBJ_TO_PTR(args[0]); + uint32_t block_num = mp_obj_get_int(args[1]); + uint32_t offset = block_num * self->block_size; + mp_buffer_info_t bufinfo; + int ret; + uint32_t FlashAddrss = self->info.LfsStartAddress + offset; + + if (n_args == 3) { + ret = Helios_Flash_Erase(FlashAddrss, self->block_size); + if (ret) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + } else { + offset += mp_obj_get_int(args[3]); + } + FlashAddrss = self->info.LfsStartAddress + offset; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + ret = Helios_Flash_Write(FlashAddrss, bufinfo.buf, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(ret); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flash_writeblocks_obj, 3, 4, flash_writeblocks); + +static mp_obj_t flash_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in) { + flash_device_obj_t *self = self_in; + mp_int_t cmd = mp_obj_get_int(cmd_in); + mp_int_t block_num = mp_obj_get_int(arg_in); + int ret; + uint32_t FlashAddrss; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_INIT: + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + return MP_OBJ_NEW_SMALL_INT(0); + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + return MP_OBJ_NEW_SMALL_INT(self->block_count); + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + return MP_OBJ_NEW_SMALL_INT(self->block_size); + + case MP_BLOCKDEV_IOCTL_BLOCK_ERASE: + FlashAddrss = self->info.LfsStartAddress + (block_num * self->block_size); + ret = Helios_Flash_Erase(FlashAddrss, self->block_size); + return MP_OBJ_NEW_SMALL_INT(ret); + + default: + return MP_OBJ_NEW_SMALL_INT(-1); + } +} +static MP_DEFINE_CONST_FUN_OBJ_3(flash_ioctl_obj, flash_ioctl); + +static const mp_obj_dict_t flash_device_locals_dict; + +MP_DEFINE_CONST_OBJ_TYPE( + flash_device_type, + MP_QSTR_FlashDevice, + MP_TYPE_FLAG_NONE, + make_new, flash_init, + locals_dict, &flash_device_locals_dict + ); + +static const mp_rom_map_elem_t flash_device_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_myflash) }, + { MP_ROM_QSTR(MP_QSTR_FlashDevice), MP_ROM_PTR(&flash_device_type)}, + { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&flash_readblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&flash_writeblocks_obj) }, + { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&flash_ioctl_obj) }, +}; + +static MP_DEFINE_CONST_DICT(flash_device_locals_dict, flash_device_locals_dict_table); + +const mp_obj_module_t flashdev_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&flash_device_locals_dict, +}; + +MP_REGISTER_MODULE(MP_QSTR_flashdev, flashdev_module); diff --git a/ports/quectel/modhelios.c b/ports/quectel/modhelios.c new file mode 100644 index 0000000000000..68cacceea5c06 --- /dev/null +++ b/ports/quectel/modhelios.c @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 +#include + +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#include "quectel_version.h" + +static mp_obj_t helios_platform(void) { + char platform[64] = {0}; + snprintf(platform, sizeof(platform), "%s%d.%d.%d", "heliossdk-v", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO); + return mp_obj_new_str(platform, strlen(platform)); +} +static MP_DEFINE_CONST_FUN_OBJ_0(helios_platform_obj, helios_platform); + +static const mp_rom_map_elem_t helios_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_helios) }, + + { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&helios_platform_obj) }, +}; + +static MP_DEFINE_CONST_DICT(helios_module_globals, helios_module_globals_table); + +const mp_obj_module_t helios_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&helios_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_helios, helios_module); diff --git a/ports/quectel/modtime.c b/ports/quectel/modtime.c new file mode 100644 index 0000000000000..e008822e97d1d --- /dev/null +++ b/ports/quectel/modtime.c @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 +#include +#include +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" + +#include "helios_debug.h" +#include "helios_rtc.h" + +uint64_t mp_time_start_second; + +static void mp_time_localtime_get(timeutils_struct_time_t *tm) { + Helios_RTCTime rtc_tm; + Helios_RTC_GetLocalTime(&rtc_tm); + tm->tm_year = rtc_tm.tm_year; + tm->tm_mon = rtc_tm.tm_mon; + tm->tm_mday = rtc_tm.tm_mday; + tm->tm_hour = rtc_tm.tm_hour; + tm->tm_min = rtc_tm.tm_min; + tm->tm_sec = rtc_tm.tm_sec; + tm->tm_wday = rtc_tm.tm_wday; + tm->tm_yday = 0; +} + +// return rtc seconds since power on +static mp_obj_t mp_time_time_get(void) { + uint64_t seconds = Helios_RTC_GetSecond() - mp_time_start_second; + return mp_obj_new_int_from_ull(seconds); +} + +uint64_t mp_hal_time_ns(void) { + return (Helios_RTC_GetSecond() - mp_time_start_second) * 1000000000ULL; +} diff --git a/ports/quectel/modules/_boot.py b/ports/quectel/modules/_boot.py new file mode 100644 index 0000000000000..22b8e85164ba7 --- /dev/null +++ b/ports/quectel/modules/_boot.py @@ -0,0 +1,67 @@ +# Copyright (c) Quectel Wireless Solution, Co., Ltd.All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import ujson +import flashdev + +""" +Mark.Zhu - 2022/12/02 +Added the littlefs2 mount function,it will be called after a failed attempt to mount littlefs1 +""" +# global datacall_flag +datacall_flag = 1 + + +def _repl_enable(): + global datacall_flag + if "system_config.json" in os.listdir("/"): + with open("/system_config.json", "r", encoding="utf-8") as fd: + try: + json_data = ujson.load(fd) + datacall_flag = json_data.get("datacallFlag", 1) + except ValueError: + with open("/usr/system_config.json", "w", encoding="utf-8") as fdw: + new_json_data = ujson.dumps({"replFlag": 0, "datacallFlag": datacall_flag}) + fdw.write(new_json_data) + else: + with open("/system_config.json", "w+", encoding="utf-8") as fd: + repl_data = ujson.dumps({"replFlag": 0}) + fd.write(repl_data) + + +try: + udev = None + try: + from os import VfsLfs1 as VfsLfs + except Exception: + from os import VfsLfs2 as VfsLfs + dev = flashdev.FlashDevice("customer_fs", 4096) + try: + udev = os.VfsLfs2(dev) + os.mount(udev, "/") + except Exception: + os.VfsLfs2.mkfs(dev) + udev = os.VfsLfs2(dev) + os.mount(udev, "/") + if "usr" not in os.listdir(): + os.mkdir("usr") + if "bak" not in os.listdir(): + os.mkdir("bak") + +except Exception: + print("error occurs in boot step.") + +finally: + _repl_enable() diff --git a/ports/quectel/modules/manifest.py b/ports/quectel/modules/manifest.py new file mode 100644 index 0000000000000..c80309cf675a2 --- /dev/null +++ b/ports/quectel/modules/manifest.py @@ -0,0 +1 @@ +freeze("$(PORT_DIR)/modules") diff --git a/ports/quectel/mpconfigport.h b/ports/quectel/mpconfigport.h new file mode 100644 index 0000000000000..937151f88bd8e --- /dev/null +++ b/ports/quectel/mpconfigport.h @@ -0,0 +1,166 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 + +#include "mpconfigboard.h" + +/****************************** user define ************************************/ +// python grammar +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_CPYTHON_COMPAT (1) + +// builtin op +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_TEXT quecpython_help_text +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) + +// modules +#define MICROPY_PY_IO (1) +#define MICROPY_PY_JSON (1) +#define MICROPY_VFS (1) +#define MICROPY_PY_VFS (0) +#define MICROPY_VFS_LFS2 (1) +#define MICROPY_PY_TIME (1) +#define MICROPY_PY_HEAPQ (0) +#define MICROPY_PY_OS (1) +#define MICROPY_PY_PLATFORM (0) +#define MICROPY_PY_SELECT (0) +#define MICROPY_PY_FRAMEBUF (0) +#define MICROPY_PY_ASYNCIO (0) +#define MICROPY_PY_BINASCII_CRC32 (0) +#define MICROPY_PY_UCTYPES (0) +#define MICROPY_PY_DEFLATE (0) +#define MICROPY_OPT_COMPUTED_GOTO (1) + +#define MICROPY_PY_TIME_TIME_TIME_NS (1) +#define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1) +#define MICROPY_PY_TIME_INCLUDEFILE "ports/quectel/modtime.c" + +/******************************** base define **********************************/ +// options to control how MicroPython is built +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// You can disable the built-in MicroPython compiler by setting the following +// config option to 0. If you do this then you won't get a REPL prompt, but you +// will still be able to execute pre-compiled scripts, compiled with mpy-cross. +#define MICROPY_ENABLE_COMPILER (1) + +#define MICROPY_ENABLE_GC (1) +#define MICROPY_PY_GC (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_ENABLE_EXTERNAL_IMPORT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) // decimal numbers support +#define MICROPY_USE_INTERNAL_PRINTF (0) +#define MPZ_DIG_SIZE (16) +#define MICROPY_PY_SYS (1) + +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) + +#define MICROPY_ERROR_REPORTING MICROPY_ERROR_REPORTING_NORMAL +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_STATIC_NODES (0) +#define MICROPY_SCHEDULER_DEPTH (64)// 8 +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_THREAD (1) +#define MICROPY_READER_VFS (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_PY_SOFT_RESET (0) +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +#ifndef SSIZE_MAX +#define SSIZE_MAX 0xFFFFFFFF +#endif + +#if defined(PLAT_Unisoc) +#define MICROPY_PY_REPL_PASSWORD_PROTECT (1) +#endif + +// type definitions for the specific machine + +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size +typedef long mp_off_t; +#define UINT_FMT "%lu" +#define INT_FMT "%ld" + +#if MICROPY_ENABLE_GC +#ifndef MICROPY_QPY_GC_HEAP_SIZE +#define MICROPY_QPY_GC_HEAP_SIZE (512 * 1024) +#endif +#define MICROPY_GC_HEAP_SIZE (MICROPY_QPY_GC_HEAP_SIZE) +#endif + +#ifdef MICROPY_QPY_MAIN_TASK_STACK_SIZE +#define MP_QPY_TASK_STACK_SIZE (MICROPY_QPY_MAIN_TASK_STACK_SIZE) +#else +#define MP_QPY_TASK_STACK_SIZE (64 * 1024) +#endif + +// We need to provide a declaration/definition of alloca() +#include + +#define STRINGIFY_VALUE(s) STRINGIFY(s) +#define STRINGIFY(s) #s +#define MICROPY_HW_BOARD_NAME STRINGIFY_VALUE(BOARD) +#define MICROPY_HW_MCU_NAME "QUECTEL" +// board specifics +#define MICROPY_PY_SYS_PLATFORM STRINGIFY_VALUE(BOARD) +#define QUECPYTHON_VERSION_STRING STRINGIFY_VALUE(QPYVER) + +#ifdef __linux__ +#define MICROPY_MIN_USE_STDOUT (1) +#endif + +#ifdef __thumb__ +#define MICROPY_MIN_USE_CORTEX_CPU (0) +#define MICROPY_MIN_USE_STM32_MCU (0) +#endif + +#define MP_STATE_PORT MP_STATE_VM + +#if MICROPY_PY_THREAD +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(mp_handle_pending_behaviour_t behavior); \ + mp_handle_pending(true); \ + MP_THREAD_GIL_EXIT(); \ + MP_THREAD_GIL_ENTER(); \ + } while (0); +#else +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(mp_handle_pending_behaviour_t behavior); \ + mp_handle_pending(true); \ + asm ("waiti 0"); \ + } while (0); +#endif diff --git a/ports/quectel/mphalport.c b/ports/quectel/mphalport.c new file mode 100644 index 0000000000000..629f51a0dc2f1 --- /dev/null +++ b/ports/quectel/mphalport.c @@ -0,0 +1,341 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 +#include +#include +#include "py/stream.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/builtin.h" +#include "py/parse.h" +#include "py/ringbuf.h" +#include "py/mpthread.h" +#include "mphalport.h" + +#include "mpconfigport.h" +#include "helios_uart.h" +#include "helios_rtc.h" +#include "shared/runtime/interrupt_char.h" +#include "py/runtime.h" + +#define QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM (2 * MICROPY_SCHEDULER_DEPTH) +static Helios_MsgQ_t qpy_mthread_sleep_queue = 0; +static int mthread_sleep_flag = 0; + +#define MTHREAD_SLEEP_ENTER() (mthread_sleep_flag = 1) +#define MTHREAD_SLEEP_EXIT() (mthread_sleep_flag = 0) +#define IS_MTHREAD_IN_SLEEP() (1 == mthread_sleep_flag) +#define MP_HAL_PORT_CHECK_OPEN (mp_hal_cdcPort_State == 1) + +#if !defined(MICROPY_PY_RANDOM_SEED_INIT_FUNC) +#define MICROPY_PY_RANDOM_SEED_INIT_FUNC Helios_RTC_GetSecond() +#endif + +static uint8_t stdin_ringbuf_array[256]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array), 0, 0}; + +// mia.zhong @20220308 fix input of multi thread dump problem. +Input_ListNode_t *Head_node = NULL; +static uint8_t mp_hal_cdcPort_State = 1; + + +static bool mp_mthread_sleep_deal_is_inited(void); +static void mp_hal_stdin_cb(uint64_t ind_type, Helios_UARTNum port, uint64_t size); + + +void mp_hal_port_open(uint8_t state) { + mp_hal_cdcPort_State = state; + Helios_UARTConfig UARTConfig = { + HELIOS_UART_BAUD, + HELIOS_UART_DATABIT_8, + HELIOS_UART_STOP_1, + HELIOS_UART_PARITY_NONE, + HELIOS_UART_FC_NONE + }; + Helios_UARTInitStruct UARTInitStruct = {&UARTConfig, mp_hal_stdin_cb}; + + if (state == 1) { + Helios_UART_Deinit(QPY_REPL_UART); + if (Helios_UART_Init(QPY_REPL_UART, &UARTInitStruct)) { + mp_hal_cdcPort_State = 0; + } + } +} + + +// mia.zhong @20220308 fix input of multi thread dump problem. +static bool mp_mthread_sleep_deal_is_inited_child(Input_ListNode_t *node) { + return 0 != node->msg_q; +} + +// mia.zhong @20220308 fix input of multi thread dump problem. +int mp_mthread_sleep_child(uint32_t ms) { + mp_uint_t msg; + int ret = -1; + if (Head_node) { + if (mp_mthread_sleep_deal_is_inited_child((Input_ListNode_t *)Head_node)) { + Head_node->mthread_sleep_flag = 1; + ret = Helios_MsgQ_Get(Head_node->msg_q, (mp_uint_t *)&msg, sizeof(msg), ms); + Head_node->mthread_sleep_flag = 0; + } + } + return ret; +} +// mia.zhong @20220308 fix input of multi thread dump problem. +void mp_mthread_wakeup_child(void) { + mp_uint_t msg = 0; + if (Head_node) { + if (mp_mthread_sleep_deal_is_inited_child((Input_ListNode_t *)Head_node) && (Head_node->mthread_sleep_flag == 1)) { + Helios_MsgQ_Put(Head_node->msg_q, (const void *)(&msg), sizeof(mp_uint_t), 0); + } + } +} + +// main thread sleep +int mp_mthread_sleep(uint32_t ms) { + mp_uint_t msg; + int ret = -1; + if (mp_mthread_sleep_deal_is_inited()) { + MTHREAD_SLEEP_ENTER(); + ret = Helios_MsgQ_Get(qpy_mthread_sleep_queue, (mp_uint_t *)&msg, sizeof(msg), ms); + MTHREAD_SLEEP_EXIT(); + } + return ret; +} + +// wake up main sleep +void mp_mthread_wakeup(void) { + mp_uint_t msg = 0; + if (mp_mthread_sleep_deal_is_inited() && (IS_MTHREAD_IN_SLEEP())) { + Helios_MsgQ_Put(qpy_mthread_sleep_queue, (const void *)(&msg), sizeof(mp_uint_t), 0); + } +} + +void mp_main_thread_wakeup() { + // mia.zhong @20220308 fix input of multi thread dump problem. + if (Head_node != NULL) { + mp_mthread_wakeup_child(); + } else { + mp_mthread_wakeup();// forrest.liu@20210809 add for quecpython task repl using waiting msg + } +} + +static void mp_hal_stdin_cb(uint64_t ind_type, Helios_UARTNum port, uint64_t size) { + if (MP_HAL_PORT_CHECK_OPEN) { + { + mp_main_thread_wakeup(); + } + } +} + +int mp_hal_stdio_init(void) { + Helios_UARTConfig UARTConfig = { + HELIOS_UART_BAUD_115200, + HELIOS_UART_DATABIT_8, + HELIOS_UART_STOP_1, + HELIOS_UART_PARITY_NONE, + HELIOS_UART_FC_NONE + }; + + Helios_UARTInitStruct UARTInitStruct = {&UARTConfig, mp_hal_stdin_cb}; + int ret = (int)Helios_UART_Init(QPY_REPL_UART, &UARTInitStruct); + if (ret) { + return -1; + } + return 0; +} + +int mp_hal_stdin_rx_chr(void) { + while (1) { + volatile unsigned char c = 0; + + if (MP_HAL_PORT_CHECK_OPEN && Helios_UART_Read(QPY_REPL_UART, (void *)&c, sizeof(unsigned char)) > 0) { + return c; + } + + // forrest.liu@20210809 add for quecpython task repl using waiting msg + MP_THREAD_GIL_EXIT(); + // mia.zhong @20220308 fix input of multi thread dump problem. + if (Head_node != NULL) { + mp_mthread_sleep_child(HELIOS_WAIT_FOREVER); + } else { + mp_mthread_sleep(HELIOS_WAIT_FOREVER); + } + MP_THREAD_GIL_ENTER(); + + MICROPY_EVENT_POLL_HOOK + } +} + +mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) { + if (!str || !len) { + return 0; + } + if (MP_HAL_PORT_CHECK_OPEN) { + Helios_UART_Write(QPY_REPL_UART, (void *)str, len); + } + return len; +} + +mp_uint_t mp_hal_ticks_ms(void) { + return (mp_uint_t)Helios_RTC_TicksToMs(); +} + +mp_uint_t mp_hal_ticks_us(void) { + return (mp_uint_t)Helios_RTC_TicksToUs(); +} + +static void mp_hal_random_init(void) { + static bool seeded = false; + if (!seeded) { + seeded = true; + srand(MICROPY_PY_RANDOM_SEED_INIT_FUNC); + } +} + +void mp_hal_get_random(size_t n, uint8_t *buf) { + mp_hal_random_init(); + uint32_t r = 0; + for (size_t i = 0; i < n; i++) { + if ((i & 3) == 0) { + r = rand(); // returns 32-bit random number + } + buf[i] = r; + r >>= 8; + } +} + +void mp_hal_delay_ms(mp_uint_t ms) { + mp_uint_t dt = 0; + mp_uint_t t0 = 0, t1 = 0; + Helios_Thread_t taskid = 0; + extern Helios_Thread_t ql_micropython_task_ref; + taskid = Helios_Thread_GetID(); + mp_uint_t qpy_mthread_sleep_deal_fun(mp_uint_t ms); + + if (ql_micropython_task_ref == taskid) { + for (;;) + { + t0 = mp_hal_ticks_us(); + MP_THREAD_GIL_EXIT(); + mp_uint_t wait_time = qpy_mthread_sleep_deal_fun(ms); + MP_THREAD_GIL_ENTER(); + if (wait_time >= ms) { + return; + } + MICROPY_EVENT_POLL_HOOK; + t1 = mp_hal_ticks_us(); + dt = t1 - t0; + if (dt / 1000 >= ms) { + return; + } + ms -= dt / 1000; + } + } else { + MP_THREAD_GIL_EXIT(); + Helios_msleep(ms); + MP_THREAD_GIL_ENTER(); + } +} + +void mp_hal_delay_us(mp_uint_t us) { + mp_uint_t ms = us / 1000; + ms = ms ? ms : 1; + mp_hal_delay_ms(ms); +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return (mp_uint_t)Helios_RTC_GetTicks(); +} + + +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if ((poll_flags & MP_STREAM_POLL_RD) && stdin_ringbuf.iget != stdin_ringbuf.iput) { + ret |= MP_STREAM_POLL_RD; + } + return ret; +} + +mp_uint_t qpy_mthread_sleep_deal_fun(mp_uint_t ms) { + if (mp_mthread_sleep_deal_is_inited()) { + mp_uint_t dt; + mp_uint_t t0 = mp_hal_ticks_us(); + int ret = mp_mthread_sleep(ms); + if (ret < 0) { + return ms; + } else { + mp_uint_t t1 = mp_hal_ticks_us(); + dt = t1 - t0; + return dt / 1000; + } + } else { + Helios_msleep(ms); + return ms; + } +} + +// mia.zhong @20220308 fix input of multi thread dump problem. +void _add_list_node() { + Input_ListNode_t *node = (Input_ListNode_t *)malloc(sizeof(Input_ListNode_t)); + memset(node, 0, sizeof(Input_ListNode_t)); + + if (0 == node->msg_q) { + node->msg_q = Helios_MsgQ_Create(QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM, sizeof(mp_uint_t)); + } + node->next_node = Head_node; + Head_node = node; +} + +// mia.zhong @20220308 fix input of multi thread dump problem. +void _delete_list_node() {// delete node + Input_ListNode_t *node = NULL; + + if (Head_node) { + if (0 != Head_node->msg_q) { + Helios_MsgQ_Delete(Head_node->msg_q); + } + node = Head_node; + Head_node = (Input_ListNode_t *)Head_node->next_node; + node->next_node = NULL; + if (node) { + free(node); + node = NULL; + } + } +} + +void mp_mthread_sleep_deal_init(void) { + if (0 == qpy_mthread_sleep_queue) { + qpy_mthread_sleep_queue = \ + Helios_MsgQ_Create(QPY_MTHREAD_SLEEP_DEAL_MSG_MAX_NUM, sizeof(mp_uint_t)); + } +} + +static bool mp_mthread_sleep_deal_is_inited(void) { + return 0 != qpy_mthread_sleep_queue; +} diff --git a/ports/quectel/mphalport.h b/ports/quectel/mphalport.h new file mode 100644 index 0000000000000..065ac968440f2 --- /dev/null +++ b/ports/quectel/mphalport.h @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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. + */ + +#ifndef __MPHAL_PORT_H +#define __MPHAL_PORT_H + +#include "helios_os.h" +#include "helios_uart.h" + +#define HAL_TICK1S 32.768 +#define QPY_REPL_UART HELIOS_UART3 +#define HELIOS_UART_BAUD HELIOS_UART_BAUD_115200 + +// mia.zhong @20220308 input接口多线程调用导致dump问题 +typedef struct Input_ListNode +{ + // int id; + int mthread_sleep_flag; + Helios_MsgQ_t msg_q; + void *next_node; +} Input_ListNode_t; + +void _add_list_node(); +void _delete_list_node(); +mp_uint_t mp_hal_ticks_cpu(void); + +void mp_mthread_sleep_deal_init(void); +int mp_mthread_sleep(uint32_t ms); +void mp_mthread_wakeup(void); +int mp_mthread_sleep_child(uint32_t ms); +void mp_mthread_wakeup_child(void); + +int mp_hal_stdio_init(void); + +int mp_hal_stdin_rx_chr(void); + +void mp_hal_port_open(uint8_t state); + +void mp_hal_set_interrupt_char(int c); + +static void mp_hal_random_init(void); +void mp_hal_get_random(size_t n, uint8_t *buf); + +#endif \ No newline at end of file diff --git a/ports/quectel/mpthreadport.c b/ports/quectel/mpthreadport.c new file mode 100644 index 0000000000000..f33ff7301e7e2 --- /dev/null +++ b/ports/quectel/mpthreadport.c @@ -0,0 +1,332 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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 "stdio.h" + +#include "py/runtime.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "py/mphal.h" +#include "mpthreadport.h" +#include "helios_os.h" + +#if MICROPY_PY_THREAD + +// this structure forms a linked list, one node per active thread +typedef struct _thread_t { + Helios_Thread_t id; // system id of thread + int ready; // whether the thread is ready and running + void *arg; // thread Python args, a GC root pointer + void *stack; // pointer to the stack + size_t stack_len; // number of words in the stack + struct _thread_t *next; +} thread_t; + +// the mutex controls access to the linked list +static mp_thread_mutex_t thread_mutex = 0; +static thread_t thread_entry0; +static thread_t *thread = NULL; // root pointer, handled by mp_thread_gc_others + +void mp_thread_init(void *stack, uint32_t stack_len) { + mp_thread_set_state(&mp_state_ctx.thread); + // create the first entry in the linked list of all threads + thread = &thread_entry0; + thread->id = Helios_Thread_GetID(); + thread->ready = 1; + thread->arg = NULL; + thread->stack = stack; + thread->stack_len = stack_len; + thread->next = NULL; + mp_thread_mutex_init(&thread_mutex); +} + +void _vPortCleanUpTCB(void *tcb) { + if (thread == NULL) { + // threading not yet initialised + return; + } + thread_t *prev = NULL; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { + // unlink the node from the list + if ((void *)th->id == tcb) { + if (prev != NULL) { + prev->next = th->next; + } else { + // move the start pointer + thread = th->next; + } + // explicitly release all its memory + m_del(thread_t, th, 1); + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_gc_others(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + gc_collect_root((void **)&th, 1); + gc_collect_root(&th->arg, 1); // probably not needed + if (th->id == Helios_Thread_GetID()) { + continue; + } + if (!th->ready) { + continue; + } + gc_collect_root(th->stack, th->stack_len); // probably not needed + } + mp_thread_mutex_unlock(&thread_mutex); +} + +mp_state_thread_t *mp_thread_get_state(void) { + return (mp_state_thread_t *)Helios_Thread_GetSpecific(); +} + +void mp_thread_set_state(mp_state_thread_t *state) { + Helios_Thread_SetSpecific((mp_state_thread_t *)state); +} + +void mp_thread_start(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == Helios_Thread_GetID()) { + th->ready = 1; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +static void *(*ext_thread_entry)(void *) = NULL; +static void thread_entry(void *arg) { + if (ext_thread_entry) { + ext_thread_entry(arg); + } + _vPortCleanUpTCB((void *)Helios_Thread_GetID()); + Helios_Thread_Exit(); +} + + +int mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) { + // store thread entry function into a global variable so we can access it + ext_thread_entry = entry; + if (*stack_size == 0) { + *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size + } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) { + *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size + } + + // Allocate linked-list node (must be outside thread_mutex lock) + thread_t *th = m_new_obj(thread_t); + + mp_thread_mutex_lock(&thread_mutex, 1); + + // create thread + Helios_ThreadAttr ThreadAttr = { + .name = name, + .stack_size = *stack_size / sizeof(uint32_t), + .priority = priority, + .entry = thread_entry, + .argv = arg + }; + Helios_Thread_t thread_id = Helios_Thread_Create(&ThreadAttr); + if (thread_id == 0) { + mp_thread_mutex_unlock(&thread_mutex); + mp_raise_msg(&mp_type_OSError, (mp_rom_error_text_t)"can't create thread"); + } + + // add thread to linked list of all threads + th->id = thread_id; + th->ready = 0; + th->arg = arg; + th->stack = Helios_Thread_GetStaskPtr(th->id); + + // stack_len must be 1/4 of ThreadAttr.stack_size + th->stack_len = ThreadAttr.stack_size / sizeof(uint32_t); + th->next = thread; + thread = th; + + // adjust the stack_size to provide room to recover from hitting the limit + *stack_size -= 1024; + + mp_thread_mutex_unlock(&thread_mutex); + + return (int)th;// return task_node +} + +// forrest.liu@20210408 increase the priority for that python thread can,t be scheduled +mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { + int th_node = mp_thread_create_ex(entry, arg, stack_size, (MP_THREAD_PRIORITY - 1), "mp_thread"); + return th_node; +} + +void mp_new_thread_add(uint32_t th_id, uint32_t stack_size) { + // Allocate linked-list node (must be outside thread_mutex lock) + thread_t *th = m_new_obj(thread_t); + // add thread to linked list of all threads + th->id = th_id; + th->ready = 0; + th->arg = NULL; + th->stack = Helios_Thread_GetStaskPtr((Helios_Thread_t)th->id); + + th->stack_len = stack_size / sizeof(uint32_t); + th->next = thread; + thread = th; +} + +void mp_thread_finish(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == Helios_Thread_GetID()) { + th->ready = 0; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +bool mp_thread_finish_by_threadid(int thread_id) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == (Helios_Thread_t)thread_id) { + th->ready = 0; + mp_thread_mutex_unlock(&thread_mutex); + return true; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return false; +} + +bool mp_is_python_thread(void) { + Helios_Thread_t curr_id = Helios_Thread_GetID(); + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == curr_id) { + mp_thread_mutex_unlock(&thread_mutex); + return true; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return false; +} + +int mp_thread_get_current_tsknode(void) { + Helios_Thread_t curr_id = Helios_Thread_GetID(); + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == (Helios_Thread_t)curr_id) { + mp_thread_mutex_unlock(&thread_mutex); + return (int)th; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return 0; +} + +int mp_thread_get_tskid_by_tsknode(void *th_node) { + thread_t *th_id = (thread_t *)th_node; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th == th_id) { + mp_thread_mutex_unlock(&thread_mutex); + return (int)th->id; + } + } + mp_thread_mutex_unlock(&thread_mutex); + return 0; +} + +mp_uint_t mp_thread_get_id(void) { + return (mp_uint_t)Helios_Thread_GetID(); +} + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { + *mutex = Helios_Mutex_Create(); +} + +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { + return Helios_Mutex_Lock(*mutex, wait ? QPY_WAIT_FOREVER : QPY_NO_WAIT); +} + +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { + Helios_Mutex_Unlock(*mutex); +} + +// Added by Freddy @20210818 delete a lock +void mp_thread_mutex_del(mp_thread_mutex_t *mutex) { + Helios_Mutex_Delete(*mutex); +} + +void mp_thread_semphore_init(mp_thread_semphore_t *sem, uint32_t initcount) { + *sem = Helios_Semaphore_Create(initcount, initcount); +} + +int mp_thread_semphore_acquire(mp_thread_semphore_t *sem, int wait) { + return Helios_Semaphore_Acquire(*sem, wait); +} + +void mp_thread_semphore_release(mp_thread_semphore_t *sem) { + Helios_Semaphore_Release(*sem); +} + +void mp_thread_semphore_del(mp_thread_semphore_t *sem) { + Helios_Semaphore_Delete(*sem); +} + +void mp_thread_deinit(void) { + for (;;) { + // Find a task to delete + int id = 0; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + // Don't delete the current task + if (th->id != Helios_Thread_GetID()) { + id = th->id; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); + + if (id == 0) { + // No tasks left to delete + break; + } else { + // Call qpy_thread_delete to delete the task (it will call vPortCleanUpTCB) + Helios_Thread_Delete(id); + _vPortCleanUpTCB((void *)id); + } + } +} + +unsigned int mp_get_available_memory_size(void) { + return Helios_GetAvailableMemorySize(); +} + + +#endif // MICROPY_PY_THREAD diff --git a/ports/quectel/mpthreadport.h b/ports/quectel/mpthreadport.h new file mode 100644 index 0000000000000..4ee4bf063e92a --- /dev/null +++ b/ports/quectel/mpthreadport.h @@ -0,0 +1,64 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 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. + */ + +#ifndef MICROPY_INCLUDED_MPTHREADPORT_H +#define MICROPY_INCLUDED_MPTHREADPORT_H + +#include "helios_os.h" + +#define MP_THREAD_MIN_STACK_SIZE (32 * 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) +#define MP_THREAD_PRIORITY 100 + + +typedef Helios_Mutex_t mp_thread_mutex_t; +typedef Helios_Sem_t mp_thread_semphore_t; + + +#define QPY_WAIT_FOREVER HELIOS_WAIT_FOREVER +#define QPY_NO_WAIT HELIOS_NO_WAIT + +void mp_thread_init(void *stack, uint32_t stack_len); +void mp_thread_gc_others(void); +void mp_thread_deinit(void); +unsigned int mp_get_available_memory_size(void); +bool mp_is_python_thread(void); +int mp_thread_get_current_tsknode(void); +int mp_thread_get_tskid_by_tsknode(void *th_node); + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex); +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait); +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex); +void mp_thread_mutex_del(mp_thread_mutex_t *mutex); + +void mp_thread_semphore_init(mp_thread_semphore_t *sem, uint32_t initcount); +int mp_thread_semphore_acquire(mp_thread_semphore_t *sem, int wait); +void mp_thread_semphore_release(mp_thread_semphore_t *sem); +void mp_thread_semphore_del(mp_thread_semphore_t *sem); + +void mp_new_thread_add(uint32_t th_id, uint32_t stack_size); + +#endif // MICROPY_INCLUDED_MPTHREADPORT_H \ No newline at end of file diff --git a/ports/quectel/plat.mk b/ports/quectel/plat.mk new file mode 100644 index 0000000000000..48c748053e4b2 --- /dev/null +++ b/ports/quectel/plat.mk @@ -0,0 +1,9 @@ +# plat definition + +ifeq ($(strip $(PLAT)),) +export PLAT = Unisoc +export BOARD = EG915UEC_AC + +DFLAGS = PLAT_$(strip $(PLAT)) BOARD_$(strip $(BOARD)) BOARD=$(strip $(BOARD)) +endif + diff --git a/ports/quectel/qstrdefsport.h b/ports/quectel/qstrdefsport.h new file mode 100644 index 0000000000000..00d3e2ae3c555 --- /dev/null +++ b/ports/quectel/qstrdefsport.h @@ -0,0 +1,2 @@ +// qstrs specific to this port +// *FORMAT-OFF* diff --git a/ports/quectel/quectel.mk b/ports/quectel/quectel.mk new file mode 100644 index 0000000000000..21fc88a4fb75c --- /dev/null +++ b/ports/quectel/quectel.mk @@ -0,0 +1,71 @@ +PLAT_DFLAGS = $(addprefix -D,$(DFLAGS)) +PLAT_CFLAGS = $(QUEC_CFLAGS) +PLAT_CFLAGS += -Wno-unused-parameter -Wformat=0 -Wno-unused-function + +INC += -I./boards/$(BOARD) +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/include + +INC += -I. +INC += -I$(HEADER_BUILD) +INC += -I$(TOP) +INC += -I$(TOP)/py +INC += -I$(TOP)/extmod +INC += -I$(TOP)/lib/utils +INC += -I$(TOP)/lib/mp-readline +INC += -I$(TOP)/lib/littlefs +INC += -I$(TOP)/lib/netutils +INC += -I$(TOP)/lib/timeutils +INC += -I$(TOP)/ports/quectel/core +INC += -I$(TOP)/ports/quectel/core/source +INC += -I$(TOP)/ports/quectel/core/py +INC += -I$(ROOT)/peripheral +INC += -I$(ROOT)/system/include +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/include +INC += -I$(ROOT)/system/platform/$(strip $(PLAT))/boards/$(strip $(BOARD))/include +INC += -I$(ROOT)/system/debug +INC += -I$(ROOT)/system/dev +INC += -I$(ROOT)/system/fs +INC += -I$(ROOT)/system/hal +INC += -I$(ROOT)/system/gnss +INC += -I$(ROOT)/system/network +INC += -I$(ROOT)/system/at +INC += -I$(ROOT)/system/aliiot + +ifeq ($(strip $(PLAT)),Unisoc) +INC += -I$(ROOT)/system/at +INC += -I$(ROOT)/system/esim +endif + +ifeq ($(CONFIG_MBEDTLS), 1) +INC += -I$(ROOT)/system/mbedtls +INC += -I$(ROOT)/system/mbedtls/include +INC += -I$(ROOT)/system/mbedtls/include/mbedtls +INC += -I$(ROOT)/system/mbedtls/library +INC += -I$(ROOT)/system/mbedtls/port/helios/inc +endif + +INC += -I$(ROOT)/system/network +INC += -I$(ROOT)/system/os +INC += -I$(ROOT)/system/startup +INC += -I$(ROOT)/system/fota +INC += -I$(ROOT)/system/bt +INC += -I$(ROOT)/system/at + +ifeq ($(MICROPY_VFS_QUECFS),1) +QUEC_MOD_CFLAGS += -DMICROPY_VFS_QUECFS=1 +endif + +QUEC_SRC += \ + main.c \ + mphalport.c \ + mpthreadport.c \ + help.c + +# modules source file will used by gen +QUEC_SRC_MOD += \ + gccollect.c \ + modflashdev.c \ + modhelios.c + +# py modules define +QUEC_PY_MOD = modules/manifest.py